diff --git a/modules/purchase_trade/docs/business-rules.md b/modules/purchase_trade/docs/business-rules.md
index ae10214..aae36ba 100644
--- a/modules/purchase_trade/docs/business-rules.md
+++ b/modules/purchase_trade/docs/business-rules.md
@@ -702,6 +702,18 @@ Owner technique: `a completer`
`All`, `Purchase`, `Sale`.
- Le filtre `Shipping status` porte uniquement le lien `shipment_in` du
`lot.lot` ou du `lot.qt`.
+ - Les dates de contexte `As of` et `To` filtrent les contrats via
+ `purchase.purchase_date` et `sale.sale_date`.
+ - Si le filtre `Side` vaut `Purchase`, les bornes de dates s'appliquent au
+ `purchase_date`.
+ - Si le filtre `Side` vaut `Sale`, les bornes de dates s'appliquent au
+ `sale_date`.
+ - Si le filtre `Side` vaut `All`, une ligne est conservee si son
+ `purchase_date` ou son `sale_date` entre dans les bornes.
+ - Le filtre `Book` du rapport cible une valeur de dimension analytique
+ rattachee au contrat achat ou vente via `analytic.dimension.assignment`.
+ - Le filtre `Strategy` cible les strategies MTM rattachees aux lignes achat
+ ou vente (`purchase.strategy` / `sale.strategy`).
- Resultat attendu:
- `Unshipped`: aucun `shipment_in` lie au `lot.lot` ou au `lot.qt`.
- `Scheduled`: un `shipment_in` est lie et son etat est `draft`.
diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py
index e0abdde..46ce27b 100755
--- a/modules/purchase_trade/lot.py
+++ b/modules/purchase_trade/lot.py
@@ -9,10 +9,11 @@ from trytond.tools import (
from trytond.transaction import Transaction, inactive_records
from decimal import getcontext, Decimal, ROUND_HALF_UP
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
-from sql.conditionals import Case
-from sql import Column, Literal, Union, Select
-from sql.conditionals import Coalesce
-from sql.functions import CurrentTimestamp, DateTrunc, Abs
+from sql.conditionals import Case
+from sql import Column, Literal, Union, Select
+from sql.conditionals import Coalesce
+from sql.functions import CurrentTimestamp, DateTrunc, Abs
+from sql.operators import Exists
from trytond.wizard import Button, StateTransition, StateView, Wizard, StateAction
from itertools import chain, groupby
from operator import itemgetter
@@ -1221,7 +1222,8 @@ class LotQt(
def getQuery(cls, purchase=None, sale=None, shipment=None, type=None,
state=None, qttype=None, supplier=None, client=None, ps=None,
lot_status=None, group=None, product=None, location=None,
- origin=None, finished=False, shipping_status=None):
+ origin=None, finished=False, shipping_status=None, asof=None,
+ todate=None, book=None, strategy=None):
pool = Pool()
LotQt = pool.get('lot.qt')
lqt = LotQt.__table__()
@@ -1259,10 +1261,48 @@ class LotQt(
final_s = Final_s.__table__()
FinalLine_s = pool.get('account.invoice.line')
final_line_s = FinalLine_s.__table__()
- ProductUom = pool.get('product.uom')
- puom = ProductUom.__table__()
- ProductUomLine = pool.get('product.uom')
- puoml = ProductUomLine.__table__()
+ ProductUom = pool.get('product.uom')
+ puom = ProductUom.__table__()
+ ProductUomLine = pool.get('product.uom')
+ puoml = ProductUomLine.__table__()
+ PurchaseStrategy = pool.get('purchase.strategy')
+ pstr = PurchaseStrategy.__table__()
+ SaleStrategy = pool.get('sale.strategy')
+ sstr = SaleStrategy.__table__()
+ AnalyticDimensionAssignment = pool.get('analytic.dimension.assignment')
+ ada = AnalyticDimensionAssignment.__table__()
+
+ def date_filter(purchase_date, sale_date):
+ if not asof and not todate:
+ return None
+ purchase_clause = Literal(True)
+ sale_clause = Literal(True)
+ if asof:
+ purchase_clause &= (purchase_date >= asof)
+ sale_clause &= (sale_date >= asof)
+ if todate:
+ purchase_clause &= (purchase_date <= todate)
+ sale_clause &= (sale_date <= todate)
+ if ps == 'P':
+ return purchase_clause
+ elif ps == 'S':
+ return sale_clause
+ return purchase_clause | sale_clause
+
+ def book_filter(purchase_id, sale_id):
+ if not book:
+ return None
+ purchase_book = Exists(ada.select(
+ ada.id,
+ where=((ada.purchase == purchase_id) & (ada.value == book))))
+ sale_book = Exists(ada.select(
+ ada.id,
+ where=((ada.sale == sale_id) & (ada.value == book))))
+ if ps == 'P':
+ return purchase_book
+ elif ps == 'S':
+ return sale_book
+ return purchase_book | sale_book
wh = ((lqt.lot_quantity > 0) & ((lp.lot_type == 'virtual') | (ls.lot_type == 'virtual')))
@@ -1285,8 +1325,28 @@ class LotQt(
wh &= (lqt.lot_shipment_in == shipment)
if product:
wh &= (lqt.lot_product == product)
- if location:
- wh &= (Case((lqt.lot_shipment_in > 0, si.to_location),else_=Case((lqt.lot_shipment_internal > 0, sin.to_location),else_=pu.to_location)) == location)
+ if location:
+ wh &= (Case((lqt.lot_shipment_in > 0, si.to_location),else_=Case((lqt.lot_shipment_internal > 0, sin.to_location),else_=pu.to_location)) == location)
+ date_clause = date_filter(pu.purchase_date, sa.sale_date)
+ if date_clause is not None:
+ wh &= date_clause
+ book_clause = book_filter(pu.id, sa.id)
+ if book_clause is not None:
+ wh &= book_clause
+ if strategy:
+ purchase_strategy = Exists(pstr.select(
+ pstr.id,
+ where=((pstr.line == pl.id) & (pstr.strategy == strategy))))
+ sale_strategy = Exists(sstr.select(
+ sstr.id,
+ where=((sstr.sale_line == sl.id)
+ & (sstr.strategy == strategy))))
+ if ps == 'P':
+ wh &= purchase_strategy
+ elif ps == 'S':
+ wh &= sale_strategy
+ else:
+ wh &= (purchase_strategy | sale_strategy)
if type=='matched':
wh &= ((ls.sale_line > 0) & (lp.line > 0))
elif type=='not matched':
@@ -1403,10 +1463,30 @@ class LotQt(
wh2 &= (lp.lot_shipment_in == shipment)
if product:
wh2 &= (lp.lot_product == product)
- if location:
- wh2 &= (Case((lp.lot_shipment_in > 0, si.to_location), else_=Case((lp.lot_shipment_internal > 0, sin.to_location), else_=pu.to_location)) == location)
- if type=='matched':
- wh2 &= (lp.sale_line != None)
+ if location:
+ wh2 &= (Case((lp.lot_shipment_in > 0, si.to_location), else_=Case((lp.lot_shipment_internal > 0, sin.to_location), else_=pu.to_location)) == location)
+ date_clause = date_filter(pu.purchase_date, sa.sale_date)
+ if date_clause is not None:
+ wh2 &= date_clause
+ book_clause = book_filter(pu.id, sa.id)
+ if book_clause is not None:
+ wh2 &= book_clause
+ if strategy:
+ purchase_strategy = Exists(pstr.select(
+ pstr.id,
+ where=((pstr.line == pl.id) & (pstr.strategy == strategy))))
+ sale_strategy = Exists(sstr.select(
+ sstr.id,
+ where=((sstr.sale_line == sl.id)
+ & (sstr.strategy == strategy))))
+ if ps == 'P':
+ wh2 &= purchase_strategy
+ elif ps == 'S':
+ wh2 &= sale_strategy
+ else:
+ wh2 &= (purchase_strategy | sale_strategy)
+ if type=='matched':
+ wh2 &= (lp.sale_line != None)
elif type=='not matched':
wh2 &= (lp.sale_line == None)
if shipping_status == 'unshipped':
@@ -1935,8 +2015,10 @@ class LotReport(
client = context.get('client')
ps = context.get('ps')
shipping_status = context.get('shipping_status')
- #asof = context.get('asof')
- #todate = context.get('todate')
+ asof = context.get('asof')
+ todate = context.get('todate')
+ book = context.get('book')
+ strategy = context.get('strategy')
finished = context.get('finished')
query = LotQT.getQuery(
purchase=purchase,
@@ -1953,7 +2035,11 @@ class LotReport(
location=location,
origin=origin,
finished=finished,
- shipping_status=shipping_status)
+ shipping_status=shipping_status,
+ asof=asof,
+ todate=todate,
+ book=book,
+ strategy=strategy)
return query
@classmethod
@@ -2004,6 +2090,8 @@ class LotContext(ModelView):
client = fields.Many2One('party.party', "Client")
sale = fields.Many2One('sale.sale', "Sale")
shipment = fields.Many2One('stock.shipment.in', "Shipment")
+ book = fields.Many2One('analytic.dimension.value', "Book")
+ strategy = fields.Many2One('mtm.strategy', "Strategy")
product = fields.Many2One('product.product',"Product")
location = fields.Many2One('stock.location',"Location")
type = fields.Selection([
@@ -2110,8 +2198,9 @@ class LotContext(ModelView):
fields_names = fields_names.copy() if fields_names is not None else []
for field_name in [
'purchase', 'sale', 'shipment', 'supplier', 'client',
- 'product', 'location', 'type', 'shipping_status', 'ps',
- 'state', 'wh', 'mode', 'group', 'origin', 'finished']:
+ 'book', 'strategy', 'product', 'location', 'type',
+ 'shipping_status', 'ps', 'state', 'wh', 'mode', 'group',
+ 'origin', 'finished', 'asof', 'todate']:
if field_name not in fields_names:
fields_names.append(field_name)
return super().get_context(fields_names=fields_names)
diff --git a/modules/purchase_trade/view/lot_report_context_form.xml b/modules/purchase_trade/view/lot_report_context_form.xml
index b854c3a..805d5ce 100755
--- a/modules/purchase_trade/view/lot_report_context_form.xml
+++ b/modules/purchase_trade/view/lot_report_context_form.xml
@@ -9,6 +9,10 @@
+
+
+
+
@@ -23,12 +27,16 @@
+
+
diff --git a/modules/purchase_trade/view/lot_report_list.xml b/modules/purchase_trade/view/lot_report_list.xml
index f814c75..ecf2df2 100755
--- a/modules/purchase_trade/view/lot_report_list.xml
+++ b/modules/purchase_trade/view/lot_report_list.xml
@@ -21,7 +21,7 @@
-
+