Lots Management
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
import datetime
|
||||
|
||||
|
||||
ITSA_COMPANY_NAME = 'ITSA'
|
||||
@@ -48,3 +49,79 @@ def default_itsa_unit():
|
||||
[('name', '=', 'MT')],
|
||||
])
|
||||
return record_id(unit) if unit else None
|
||||
|
||||
|
||||
def _company_from_values(values):
|
||||
company = values.get('company')
|
||||
if company and hasattr(company, 'party'):
|
||||
return company
|
||||
company_id = record_id(company) or Transaction().context.get('company')
|
||||
if not company_id:
|
||||
return None
|
||||
return Pool().get('company.company')(company_id)
|
||||
|
||||
|
||||
def _dimension(name):
|
||||
return _search_first('analytic.dimension', [
|
||||
[('code', '=', name)],
|
||||
[('name', '=', name)],
|
||||
[('code', 'ilike', '%%%s%%' % name)],
|
||||
[('name', 'ilike', '%%%s%%' % name)],
|
||||
])
|
||||
|
||||
|
||||
def _book_year_suffix(today=None):
|
||||
if today is None:
|
||||
today = Pool().get('ir.date').today()
|
||||
year = today.year
|
||||
if today < datetime.date(year, 4, 1):
|
||||
year -= 1
|
||||
return '%02d' % (year % 100)
|
||||
|
||||
|
||||
def _book_value(dimension, today=None):
|
||||
if not dimension:
|
||||
return None
|
||||
suffix = _book_year_suffix(today)
|
||||
return _search_first('analytic.dimension.value', [
|
||||
[
|
||||
('dimension', '=', record_id(dimension)),
|
||||
('code', 'ilike', '%%%s%%' % suffix),
|
||||
],
|
||||
[
|
||||
('dimension', '=', record_id(dimension)),
|
||||
('name', 'ilike', '%%%s%%' % suffix),
|
||||
],
|
||||
])
|
||||
|
||||
|
||||
def default_itsa_analytic_dimension_assignments(company=None, today=None):
|
||||
if not is_itsa_company(company):
|
||||
return []
|
||||
|
||||
assignments = []
|
||||
book = _dimension('Book')
|
||||
if book:
|
||||
value = _book_value(book, today=today)
|
||||
assignments.append({
|
||||
'dimension': record_id(book),
|
||||
'value': record_id(value) if value else None,
|
||||
})
|
||||
|
||||
strategy = _dimension('Strategy')
|
||||
if strategy:
|
||||
assignments.append({
|
||||
'dimension': record_id(strategy),
|
||||
'value': None,
|
||||
})
|
||||
|
||||
return assignments
|
||||
|
||||
|
||||
def add_itsa_analytic_dimension_defaults(values):
|
||||
if values.get('analytic_dimensions'):
|
||||
return
|
||||
company = _company_from_values(values)
|
||||
assignments = default_itsa_analytic_dimension_assignments(company=company)
|
||||
if assignments:
|
||||
values['analytic_dimensions'] = [('create', assignments)]
|
||||
|
||||
@@ -65,7 +65,6 @@ class AnalyticDimensionAssignment(ModelSQL, ModelView):
|
||||
value = fields.Many2One(
|
||||
'analytic.dimension.value',
|
||||
'Value',
|
||||
required=True,
|
||||
domain=[
|
||||
('dimension', '=', Eval('dimension')),
|
||||
],
|
||||
@@ -76,4 +75,4 @@ class AnalyticDimensionAssignment(ModelSQL, ModelView):
|
||||
'purchase.purchase',
|
||||
'Purchase',
|
||||
ondelete='CASCADE'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1253,21 +1253,75 @@ class LotQt(
|
||||
('delivered', 'Delivered')
|
||||
], 'Where')
|
||||
lot_visible = fields.Boolean("Visible")
|
||||
lot_shipment_origin = fields.Function(
|
||||
fields.Reference(
|
||||
selection=[
|
||||
("stock.shipment.in", "In"),
|
||||
("stock.shipment.out", "Out"),
|
||||
lot_shipment_origin = fields.Function(
|
||||
fields.Reference(
|
||||
selection=[
|
||||
("stock.shipment.in", "In"),
|
||||
("stock.shipment.out", "Out"),
|
||||
("stock.shipment.internal", "Internal"),
|
||||
],
|
||||
string="Shipment",
|
||||
),
|
||||
"get_shipment_origin",
|
||||
)
|
||||
|
||||
def get_shipment_origin(self, name):
|
||||
if self.lot_shipment_in:
|
||||
return 'stock.shipment.in,' + str(self.lot_shipment_in.id)
|
||||
),
|
||||
"get_shipment_origin",
|
||||
)
|
||||
purchase = fields.Function(
|
||||
fields.Many2One('purchase.purchase', "Purchase"), 'get_purchase')
|
||||
sale = fields.Function(
|
||||
fields.Many2One('sale.sale', "Sale"), 'get_sale')
|
||||
purchase_reference = fields.Function(
|
||||
fields.Char("Purchase Ref."), 'get_purchase_reference')
|
||||
sale_reference = fields.Function(
|
||||
fields.Char("Sale Ref."), 'get_sale_reference')
|
||||
purchase_line_price = fields.Function(
|
||||
fields.Numeric("Purchase Price", digits=(16, 4)),
|
||||
'get_purchase_line_price')
|
||||
sale_line_price = fields.Function(
|
||||
fields.Numeric("Sale Price", digits=(16, 4)),
|
||||
'get_sale_line_price')
|
||||
|
||||
def _purchase_line(self):
|
||||
return getattr(getattr(self, 'lot_p', None), 'line', None)
|
||||
|
||||
def _sale_line(self):
|
||||
return getattr(getattr(self, 'lot_s', None), 'sale_line', None)
|
||||
|
||||
@staticmethod
|
||||
def _contract_reference(contract):
|
||||
if not contract:
|
||||
return None
|
||||
return (
|
||||
getattr(contract, 'reference', None)
|
||||
or getattr(contract, 'our_reference', None))
|
||||
|
||||
def get_purchase(self, name):
|
||||
line = self._purchase_line()
|
||||
purchase = getattr(line, 'purchase', None)
|
||||
return purchase.id if purchase else None
|
||||
|
||||
def get_sale(self, name):
|
||||
line = self._sale_line()
|
||||
sale = getattr(line, 'sale', None)
|
||||
return sale.id if sale else None
|
||||
|
||||
def get_purchase_reference(self, name):
|
||||
line = self._purchase_line()
|
||||
return self._contract_reference(getattr(line, 'purchase', None))
|
||||
|
||||
def get_sale_reference(self, name):
|
||||
line = self._sale_line()
|
||||
return self._contract_reference(getattr(line, 'sale', None))
|
||||
|
||||
def get_purchase_line_price(self, name):
|
||||
line = self._purchase_line()
|
||||
return getattr(line, 'unit_price', None)
|
||||
|
||||
def get_sale_line_price(self, name):
|
||||
line = self._sale_line()
|
||||
return getattr(line, 'unit_price', None)
|
||||
|
||||
def get_shipment_origin(self, name):
|
||||
if self.lot_shipment_in:
|
||||
return 'stock.shipment.in,' + str(self.lot_shipment_in.id)
|
||||
elif self.lot_shipment_out:
|
||||
return 'stock.shipment.out,' + str(self.lot_shipment_out.id)
|
||||
elif self.lot_shipment_internal:
|
||||
@@ -2725,6 +2779,16 @@ class LotReport(
|
||||
r_lot_unit_line = fields.Function(fields.Many2One('product.uom', "Unit"),'get_unit')
|
||||
r_lot_price = fields.Function(fields.Numeric("Price", digits='r_lot_unit_line'),'get_lot_price')
|
||||
r_lot_price_sale = fields.Function(fields.Numeric("Price", digits='r_lot_unit_line'),'get_lot_sale_price')
|
||||
r_purchase_reference = fields.Function(
|
||||
fields.Char("Purchase Ref."), 'get_purchase_reference')
|
||||
r_sale_reference = fields.Function(
|
||||
fields.Char("Sale Ref."), 'get_sale_reference')
|
||||
r_purchase_line_price = fields.Function(
|
||||
fields.Numeric("Purchase Price", digits=(16, 4)),
|
||||
'get_purchase_line_price')
|
||||
r_sale_line_price = fields.Function(
|
||||
fields.Numeric("Sale Price", digits=(16, 4)),
|
||||
'get_sale_line_price')
|
||||
r_del_period = fields.Many2One(
|
||||
'product.month', "Purchase Shipment Period")
|
||||
r_sale_del_period = fields.Many2One(
|
||||
@@ -2941,17 +3005,37 @@ class LotReport(
|
||||
price = premium + l.unit_price
|
||||
return round(price,2)
|
||||
|
||||
def get_lot_sale_price(self,name=None):
|
||||
price = Decimal(0)
|
||||
# if self.sale_line:
|
||||
# l = Pool().get('sale.line')(self.sale_line)
|
||||
def get_lot_sale_price(self,name=None):
|
||||
price = Decimal(0)
|
||||
# if self.sale_line:
|
||||
# l = Pool().get('sale.line')(self.sale_line)
|
||||
# premium = Decimal(0)
|
||||
# if self.lot_premium_sale:
|
||||
# premium = self.lot_premium_sale
|
||||
# price = premium + l.unit_price
|
||||
return round(price,2)
|
||||
|
||||
#@classmethod
|
||||
# price = premium + l.unit_price
|
||||
return round(price,2)
|
||||
|
||||
@staticmethod
|
||||
def _contract_reference(contract):
|
||||
if not contract:
|
||||
return None
|
||||
return (
|
||||
getattr(contract, 'reference', None)
|
||||
or getattr(contract, 'our_reference', None))
|
||||
|
||||
def get_purchase_reference(self, name=None):
|
||||
return self._contract_reference(self.r_purchase)
|
||||
|
||||
def get_sale_reference(self, name=None):
|
||||
return self._contract_reference(self.r_sale)
|
||||
|
||||
def get_purchase_line_price(self, name=None):
|
||||
return getattr(self.r_line, 'unit_price', None)
|
||||
|
||||
def get_sale_line_price(self, name=None):
|
||||
return getattr(self.r_sale_line, 'unit_price', None)
|
||||
|
||||
#@classmethod
|
||||
#def delete(cls, records):
|
||||
# Unmatch = Pool().get('lot.unmatch', type='wizard')
|
||||
# session_id, _, _ = Unmatch.create()
|
||||
@@ -3853,8 +3937,8 @@ class LotMatchingStart(ModelView):
|
||||
val = {}
|
||||
val['lot_id'] = r['r_lot_s']
|
||||
val['lot_r_id'] = r['id'] - 10000000
|
||||
val['lot_sale'] = r['r_sale']
|
||||
val['lot_purchase'] = r['r_purchase']
|
||||
val['lot_sale'] = r['r_sale']
|
||||
val['lot_purchase'] = r['r_purchase']
|
||||
val['lot_shipment_in'] = r['r_lot_shipment_in']
|
||||
val['lot_shipment_internal'] = r['r_lot_shipment_internal']
|
||||
val['lot_shipment_out'] = r['r_lot_shipment_out']
|
||||
@@ -3866,10 +3950,15 @@ class LotMatchingStart(ModelView):
|
||||
val['lot_matched_qt'] = 0
|
||||
val['lot_cp'] = r['r_client']
|
||||
lqt = LotQt(r['id'] - 10000000)
|
||||
line = LotQt._matching_line(lqt, 'sale')
|
||||
sale = getattr(line, 'sale', None)
|
||||
val['lot_sale_line_price'] = getattr(line, 'unit_price', None)
|
||||
val['lot_sale_reference'] = (
|
||||
getattr(sale, 'reference', None)
|
||||
or getattr(sale, 'our_reference', None))
|
||||
val['lot_already_matched_qt'] = (
|
||||
LotQt._matching_already_matched(lqt, 'sale'))
|
||||
val.update(LotQt._matching_line_tolerance_info(
|
||||
LotQt._matching_line(lqt, 'sale')))
|
||||
val.update(LotQt._matching_line_tolerance_info(line))
|
||||
lp.append(val)
|
||||
return lp
|
||||
|
||||
@@ -3896,10 +3985,15 @@ class LotMatchingStart(ModelView):
|
||||
val['lot_matched_qt'] = 0
|
||||
val['lot_cp'] = r['r_supplier']
|
||||
lqt = LotQt(r['id'] - 10000000)
|
||||
line = LotQt._matching_line(lqt, 'purchase')
|
||||
purchase = getattr(line, 'purchase', None)
|
||||
val['lot_purchase_line_price'] = getattr(line, 'unit_price', None)
|
||||
val['lot_purchase_reference'] = (
|
||||
getattr(purchase, 'reference', None)
|
||||
or getattr(purchase, 'our_reference', None))
|
||||
val['lot_already_matched_qt'] = (
|
||||
LotQt._matching_already_matched(lqt, 'purchase'))
|
||||
val.update(LotQt._matching_line_tolerance_info(
|
||||
LotQt._matching_line(lqt, 'purchase')))
|
||||
val.update(LotQt._matching_line_tolerance_info(line))
|
||||
lp.append(val)
|
||||
return lp
|
||||
|
||||
@@ -3910,9 +4004,15 @@ class LotMatchingLot(ModelView):
|
||||
lms = fields.Many2One('lot.matching.start',"Matching")
|
||||
lot_id = fields.Many2One('lot.lot',"Lot")
|
||||
lot_r_id = fields.Many2One('lot.qt',"Lot Qt")
|
||||
lot_purchase = fields.Many2One('purchase.purchase', "Purchase")
|
||||
lot_sale = fields.Many2One('sale.sale', "Sale")
|
||||
lot_name = fields.Char("Name",readonly=True)
|
||||
lot_purchase = fields.Many2One('purchase.purchase', "Purchase")
|
||||
lot_sale = fields.Many2One('sale.sale', "Sale")
|
||||
lot_purchase_reference = fields.Char("Purchase Ref.", readonly=True)
|
||||
lot_sale_reference = fields.Char("Sale Ref.", readonly=True)
|
||||
lot_purchase_line_price = fields.Numeric(
|
||||
"Purchase Price", digits=(16, 4), readonly=True)
|
||||
lot_sale_line_price = fields.Numeric(
|
||||
"Sale Price", digits=(16, 4), readonly=True)
|
||||
lot_name = fields.Char("Name",readonly=True)
|
||||
lot_type = fields.Selection([
|
||||
('virtual', 'Open'),
|
||||
('physic', 'Physic'),
|
||||
|
||||
@@ -23,10 +23,12 @@ import json
|
||||
import re
|
||||
import jwt
|
||||
from collections import defaultdict
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from trytond.modules.purchase_trade.numbers_to_words import quantity_to_words, amount_to_currency_words, format_date_en
|
||||
from trytond.modules.purchase_trade.company_defaults import (
|
||||
default_itsa_currency, default_itsa_unit, is_itsa_company, record_id)
|
||||
add_itsa_analytic_dimension_defaults,
|
||||
default_itsa_analytic_dimension_assignments, default_itsa_currency,
|
||||
default_itsa_unit, is_itsa_company, record_id)
|
||||
import requests
|
||||
import io
|
||||
|
||||
@@ -293,6 +295,18 @@ class Purchase(metaclass=PoolMeta):
|
||||
if purchases:
|
||||
super().process(purchases)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
for values in vlist:
|
||||
add_itsa_analytic_dimension_defaults(values)
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def default_analytic_dimensions(cls):
|
||||
default = getattr(super(), 'default_analytic_dimensions', None)
|
||||
values = default() if default else []
|
||||
return values or default_itsa_analytic_dimension_assignments()
|
||||
|
||||
btb = fields.Many2One('back.to.back',"Back to back")
|
||||
charter_conditions = fields.One2Many(
|
||||
'charter.condition', 'purchase', "Charter Conditions")
|
||||
|
||||
@@ -19,7 +19,9 @@ from collections import defaultdict
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from trytond.modules.purchase_trade.numbers_to_words import quantity_to_words, amount_to_currency_words, format_date_en
|
||||
from trytond.modules.purchase_trade.company_defaults import (
|
||||
default_itsa_currency, default_itsa_unit, is_itsa_company, record_id)
|
||||
add_itsa_analytic_dimension_defaults,
|
||||
default_itsa_analytic_dimension_assignments, default_itsa_currency,
|
||||
default_itsa_unit, is_itsa_company, record_id)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -254,6 +256,18 @@ class Sale(metaclass=PoolMeta):
|
||||
if sales:
|
||||
super().process(sales)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
for values in vlist:
|
||||
add_itsa_analytic_dimension_defaults(values)
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def default_analytic_dimensions(cls):
|
||||
default = getattr(super(), 'default_analytic_dimensions', None)
|
||||
values = default() if default else []
|
||||
return values or default_itsa_analytic_dimension_assignments()
|
||||
|
||||
btb = fields.Many2One('back.to.back',"Back to back")
|
||||
charter_conditions = fields.One2Many(
|
||||
'charter.condition', 'sale', "Charter Conditions")
|
||||
|
||||
@@ -73,9 +73,49 @@ class Move(metaclass=PoolMeta):
|
||||
blnumber = fields.Char("BL number")
|
||||
lotqt = fields.One2Many('lot.qt','lot_move',"Lots")
|
||||
lot = fields.Many2One('lot.lot',"Lot")
|
||||
|
||||
def get_linked_transit_move(self):
|
||||
if self.from_location.is_transit():
|
||||
trade_purchase = fields.Function(
|
||||
fields.Many2One('purchase.purchase', "Purchase"), 'get_trade_purchase')
|
||||
trade_sale = fields.Function(
|
||||
fields.Many2One('sale.sale', "Sale"), 'get_trade_sale')
|
||||
trade_purchase_reference = fields.Function(
|
||||
fields.Char("Purchase Ref."), 'get_trade_purchase_reference')
|
||||
trade_sale_reference = fields.Function(
|
||||
fields.Char("Sale Ref."), 'get_trade_sale_reference')
|
||||
|
||||
def _trade_purchase_line(self):
|
||||
return getattr(getattr(self, 'lot', None), 'line', None)
|
||||
|
||||
def _trade_sale_line(self):
|
||||
return getattr(getattr(self, 'lot', None), 'sale_line', None)
|
||||
|
||||
@staticmethod
|
||||
def _contract_reference(contract):
|
||||
if not contract:
|
||||
return None
|
||||
return (
|
||||
getattr(contract, 'reference', None)
|
||||
or getattr(contract, 'our_reference', None))
|
||||
|
||||
def get_trade_purchase(self, name):
|
||||
line = self._trade_purchase_line()
|
||||
purchase = getattr(line, 'purchase', None)
|
||||
return purchase.id if purchase else None
|
||||
|
||||
def get_trade_sale(self, name):
|
||||
line = self._trade_sale_line()
|
||||
sale = getattr(line, 'sale', None)
|
||||
return sale.id if sale else None
|
||||
|
||||
def get_trade_purchase_reference(self, name):
|
||||
line = self._trade_purchase_line()
|
||||
return self._contract_reference(getattr(line, 'purchase', None))
|
||||
|
||||
def get_trade_sale_reference(self, name):
|
||||
line = self._trade_sale_line()
|
||||
return self._contract_reference(getattr(line, 'sale', None))
|
||||
|
||||
def get_linked_transit_move(self):
|
||||
if self.from_location.is_transit():
|
||||
Move = Pool().get('stock.move')
|
||||
Location = Pool().get('stock.location')
|
||||
moves = Move.search([('lot','=',self.lot),('to_location','=',Location.get_transit_id())],order=[('id', 'DESC')],limit=1)
|
||||
|
||||
@@ -17,6 +17,7 @@ from trytond.modules.purchase_trade import purchase as purchase_module
|
||||
from trytond.modules.purchase_trade import sale as sale_module
|
||||
from trytond.modules.purchase_trade import stock as stock_module
|
||||
from trytond.modules.purchase_trade import fee as fee_module
|
||||
from trytond.modules.purchase_trade import company_defaults
|
||||
from trytond.modules.purchase_trade.service import ContractFactory
|
||||
|
||||
|
||||
@@ -24,6 +25,15 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
'Test purchase_trade module'
|
||||
module = 'purchase_trade'
|
||||
|
||||
def test_itsa_book_year_suffix_uses_april_fiscal_start(self):
|
||||
'ITSA book year changes on April 1st'
|
||||
self.assertEqual(
|
||||
company_defaults._book_year_suffix(datetime.date(2026, 3, 31)),
|
||||
'25')
|
||||
self.assertEqual(
|
||||
company_defaults._book_year_suffix(datetime.date(2026, 4, 1)),
|
||||
'26')
|
||||
|
||||
@with_transaction()
|
||||
def test_purchase_list_fields_use_first_trade_line(self):
|
||||
'purchase list helper fields use the first real purchase line'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<tree editable="1">
|
||||
<field name="lot_id" width="70"/>
|
||||
<field name="lot_purchase"/>
|
||||
<field name="lot_purchase_reference"/>
|
||||
<field name="lot_purchase_line_price"/>
|
||||
<field name="lot_type" width="80"/>
|
||||
<field name="lot_cp"/>
|
||||
<field name="lot_product"/>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<tree editable="1">
|
||||
<field name="lot_id" width="70"/>
|
||||
<field name="lot_sale"/>
|
||||
<field name="lot_sale_reference"/>
|
||||
<field name="lot_sale_line_price"/>
|
||||
<field name="lot_type" width="80"/>
|
||||
<field name="lot_cp"/>
|
||||
<field name="lot_product"/>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<tree>
|
||||
<field name="purchase"/>
|
||||
<field name="purchase_reference"/>
|
||||
<field name="sale"/>
|
||||
<field name="sale_reference"/>
|
||||
<field name="lot_p"/>
|
||||
<field name="lot_s"/>
|
||||
<field name="planned_from_location"/>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<field name="r_sale_del_period" string="Sale Sh. period" width="110"/>
|
||||
<field name="r_supplier" width="90"/>
|
||||
<field name="r_purchase" width="120"/>
|
||||
<field name="r_purchase_reference" width="120"/>
|
||||
<field name="r_purchase_line_price" width="100"/>
|
||||
<field name="r_lot_pur_inv" width="120"/>
|
||||
<field name="r_sale" width="120"/>
|
||||
<field name="r_sale_reference" width="120"/>
|
||||
<field name="r_sale_line_price" width="100"/>
|
||||
<field name="r_client" width="90"/>
|
||||
<field name="r_lot_sal_inv" width="120"/>
|
||||
<field name="r_lot_state" width="60"/>
|
||||
|
||||
@@ -4,6 +4,10 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='planned_date']" position="replace">
|
||||
<field name="lot" optional="0"/>
|
||||
<field name="trade_purchase" optional="0"/>
|
||||
<field name="trade_purchase_reference" optional="0"/>
|
||||
<field name="trade_sale" optional="0"/>
|
||||
<field name="trade_sale_reference" optional="0"/>
|
||||
<field name="bldate" optional="0"/>
|
||||
<field name="blnumber" optional="0"/>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user