Initial import from Docker volume
This commit is contained in:
22
modules/lot/__init__.py
Executable file
22
modules/lot/__init__.py
Executable file
@@ -0,0 +1,22 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
|
||||
from . import lot
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
lot.Lot,
|
||||
lot.QtHist,
|
||||
lot.QtType,
|
||||
lot.LotSplitMerge,
|
||||
lot.SplitWizardStart,
|
||||
lot.MergeLotsStart,
|
||||
lot.SplitLine,
|
||||
module='lot', type_='model')
|
||||
Pool.register(
|
||||
lot.SplitWizard,
|
||||
lot.MergeLots,
|
||||
module='lot', type_='wizard')
|
||||
BIN
modules/lot/__pycache__/__init__.cpython-311.opt-1.pyc
Normal file
BIN
modules/lot/__pycache__/__init__.cpython-311.opt-1.pyc
Normal file
Binary file not shown.
BIN
modules/lot/__pycache__/lot.cpython-311.opt-1.pyc
Normal file
BIN
modules/lot/__pycache__/lot.cpython-311.opt-1.pyc
Normal file
Binary file not shown.
14
modules/lot/build/lib/trytond/modules/lot/__init__.py
Executable file
14
modules/lot/build/lib/trytond/modules/lot/__init__.py
Executable file
@@ -0,0 +1,14 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
|
||||
from . import lot
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
lot.Lot,
|
||||
lot.QtHist,
|
||||
lot.QtType,
|
||||
module='lot', type_='model')
|
||||
286
modules/lot/build/lib/trytond/modules/lot/lot.py
Executable file
286
modules/lot/build/lib/trytond/modules/lot/lot.py
Executable file
@@ -0,0 +1,286 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Bool, Eval, Id
|
||||
from trytond.model import (ModelSQL, ModelView)
|
||||
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
|
||||
from sql import Column, Literal
|
||||
from sql.functions import CurrentTimestamp, DateTrunc
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Lot(ModelSQL, ModelView):
|
||||
"Lot"
|
||||
__name__ = 'lot.lot'
|
||||
_rec_name = 'lot_name'
|
||||
|
||||
line = fields.Many2One(
|
||||
'purchase.line', "Line", ondelete='CASCADE',
|
||||
)
|
||||
sale_line = fields.Many2One(
|
||||
'sale.line', "Line", ondelete='CASCADE',
|
||||
)
|
||||
lot_name = fields.Char("Lot")
|
||||
lot_qt = fields.Integer("Quantity",states={
|
||||
'readonly': Eval('lot_himself', True),
|
||||
},)
|
||||
lot_unit = fields.Many2One('product.uom', "Unit",required=True)
|
||||
lot_product = fields.Many2One('product.product', "Product")
|
||||
lot_type = fields.Selection([
|
||||
(None, ''),
|
||||
('delivery', 'Delivery'),
|
||||
('loss', 'Loss'),
|
||||
('receive', 'Receive')
|
||||
], 'Lot type',required=True)
|
||||
lot_status = fields.Selection([
|
||||
(None, ''),
|
||||
('forecast', 'Forecast'),
|
||||
('nom', 'Nom'),
|
||||
('adjusted', 'Adjusted'),
|
||||
('trade', 'Trade')
|
||||
], 'Lot status',required=True)
|
||||
lot_quantity = fields.Numeric("Net Qt",digits='lot_unit',required=True)
|
||||
lot_unit_line = fields.Function(fields.Many2One('product.uom', "Unit",required=True),'get_unit')
|
||||
lot_premium = fields.Numeric("Prem/Disc", digits=(1,2))
|
||||
lot_premium_sale = fields.Numeric("Prem/Disc", digits=(1,2))
|
||||
lot_shipment = fields.Many2One('stock.shipment.in', "Shipment")
|
||||
lot_gross_quantity = fields.Numeric("Gross Qt",digits='lot_unit')
|
||||
lot_parent = fields.Many2One('purchase.lot',"Parent")
|
||||
lot_childs = fields.One2Many('purchase.lot','lot_parent',"Childs")
|
||||
lot_himself = fields.Many2One('purchase.lot',"Lot")
|
||||
lot_container = fields.Char("Container")
|
||||
lot_qt_hist = fields.One2Many('purchase.lot.qt','lot',"Qt history")
|
||||
|
||||
lot_unit_line = fields.Function(fields.Many2One('product.uom', "Unit"),'get_unit')
|
||||
lot_price = fields.Function(fields.Numeric("Price", digits=(1,2)),'get_lot_price')
|
||||
lot_price_sale = fields.Function(fields.Numeric("Price", digits=(1,2)),'get_lot_sale_price')
|
||||
|
||||
# lot_tu_net_qt = fields.Numeric("Net Takeup Qt")
|
||||
# lot_tu_gross_qt = fields.Numeric("Gross Takeup Qt")
|
||||
# lot_wr_net_qt = fields.Numeric("Net WR Qt")
|
||||
# lot_wr_gross_qt = fields.Numeric("Gross WR Qt")
|
||||
# lot_lr_net_qt = fields.Numeric("Net LR Qt")
|
||||
# lot_lr_gross_qt = fields.Numeric("Gross LR Qt")
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Lot, cls).__setup__()
|
||||
cls._order.insert(0, ('lot_shipment', 'ASC'))
|
||||
|
||||
|
||||
@classmethod
|
||||
def default_lot_unit(cls):
|
||||
return 2 #kg
|
||||
|
||||
@classmethod
|
||||
def default_lot_qt(cls):
|
||||
return Decimal(0)
|
||||
|
||||
@classmethod
|
||||
def default_lot_gross_quantity(cls):
|
||||
return Decimal(0)
|
||||
|
||||
@classmethod
|
||||
def default_lot_status(cls):
|
||||
return 'trade'
|
||||
|
||||
@classmethod
|
||||
def default_lot_type(cls):
|
||||
return 'receive'
|
||||
|
||||
def get_unit(self,name):
|
||||
return 2 #kg
|
||||
|
||||
def get_lot_price(self,name=None):
|
||||
price = Decimal(0)
|
||||
if self.line:
|
||||
l = Pool().get('purchase.line')(self.line)
|
||||
premium = Decimal(0)
|
||||
if self.lot_premium:
|
||||
premium = self.lot_premium
|
||||
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)
|
||||
# premium = Decimal(0)
|
||||
# if self.lot_premium_sale:
|
||||
# premium = self.lot_premium_sale
|
||||
# price = premium + l.unit_price
|
||||
return round(price,2)
|
||||
|
||||
def get_sale_amount(self,name):
|
||||
round_context = getcontext()
|
||||
round_context.rounding = ROUND_HALF_UP
|
||||
price = self.get_lot_sale_price()
|
||||
# if price and not self.lot_wr_net_qt:
|
||||
# return round(price * self.lot_quantity * Decimal(2.2046),2)
|
||||
# elif price and self.lot_wr_net_qt:
|
||||
# return round(price * self.lot_wr_net_qt * Decimal(2.2046),2)
|
||||
return None
|
||||
|
||||
def get_amount(self,name):
|
||||
round_context = getcontext()
|
||||
round_context.rounding = ROUND_HALF_UP
|
||||
price = self.get_lot_price()
|
||||
# if price and not self.lot_wr_net_qt:
|
||||
# return round(price * self.lot_quantity * Decimal(2.2046),2)
|
||||
# elif price and self.lot_wr_net_qt:
|
||||
# return round(price * self.lot_wr_net_qt * Decimal(2.2046),2)
|
||||
return None
|
||||
|
||||
# @classmethod
|
||||
# def write(cls, *args):
|
||||
# super().write(*args)
|
||||
# lots = sum(args[::2], [])
|
||||
# to_write = []
|
||||
# for l in lots:
|
||||
# lotnetqt = Decimal(0)
|
||||
# lotgrossqt = Decimal(0)
|
||||
# if l.lot_lr_net_qt and l.lot_lr_net_qt > 0 and l.lot_lr_net_qt != l.lot_quantity:
|
||||
# lotnetqt = l.lot_lr_net_qt
|
||||
# lotgrossqt = l.lot_lr_gross_qt
|
||||
# elif l.lot_wr_net_qt and not l.lot_lr_net_qt and l.lot_wr_net_qt > 0 and l.lot_wr_net_qt != l.lot_quantity:
|
||||
# lotnetqt = l.lot_wr_net_qt
|
||||
# lotgrossqt = l.lot_wr_gross_qt
|
||||
# elif l.lot_tu_net_qt and not l.lot_lr_net_qt and not l.lot_wr_net_qt and l.lot_tu_net_qt > 0 and l.lot_tu_net_qt != l.lot_quantity:
|
||||
# lotnetqt = l.lot_tu_net_qt
|
||||
# lotgrossqt = l.lot_tu_gross_qt
|
||||
# if lotnetqt:
|
||||
# to_write.extend(([l], {
|
||||
# 'lot_quantity': lotnetqt,
|
||||
# 'lot_final_quantity': lotgrossqt,
|
||||
# }))
|
||||
# if to_write:
|
||||
# cls.write(*to_write)
|
||||
|
||||
# @classmethod
|
||||
# def create(cls, vlist):
|
||||
# pool = Pool()
|
||||
# vlist = [x.copy() for x in vlist]
|
||||
# for values in vlist:
|
||||
# parent = values.get('lot_parent')
|
||||
# if parent:
|
||||
# L = pool.get('position.lot')
|
||||
# l = L(parent)
|
||||
# balenw = round(Decimal((l.lot_quantity if l.lot_quantity else 0)/l.lot_bale),2)
|
||||
# balegw = round(Decimal((l.lot_final_quantity if l.lot_final_quantity else 0)/l.lot_bale),2)
|
||||
# balenwtu = round(Decimal((l.lot_tu_net_qt if l.lot_tu_net_qt else 0)/l.lot_bale),2)
|
||||
# balegwtu = round(Decimal((l.lot_tu_gross_qt if l.lot_tu_gross_qt else 0)/l.lot_bale),2)
|
||||
# balenwwr = round(Decimal((l.lot_wr_net_qt if l.lot_wr_net_qt else 0)/l.lot_bale),2)
|
||||
# balegwwr = round(Decimal((l.lot_wr_gross_qt if l.lot_wr_gross_qt else 0)/l.lot_bale),2)
|
||||
# balenwlr = round(Decimal((l.lot_lr_net_qt if l.lot_lr_net_qt else 0)/l.lot_bale),2)
|
||||
# balegwlr = round(Decimal((l.lot_lr_gross_qt if l.lot_lr_gross_qt else 0)/l.lot_bale),2)
|
||||
# nw = Decimal(int(values.get('lot_bale')) * balenw)
|
||||
# gw = Decimal(int(values.get('lot_bale')) * balegw)
|
||||
# nwtu = Decimal(int(values.get('lot_bale')) * balenwtu)
|
||||
# gwtu = Decimal(int(values.get('lot_bale')) * balegwtu)
|
||||
# nwwr = Decimal(int(values.get('lot_bale')) * balenwwr)
|
||||
# gwwr = Decimal(int(values.get('lot_bale')) * balegwwr)
|
||||
# nwlr = Decimal(int(values.get('lot_bale')) * balenwlr)
|
||||
# gwlr = Decimal(int(values.get('lot_bale')) * balegwlr)
|
||||
# l.lot_bale -= int(values.get('lot_bale'))
|
||||
# if l.lot_quantity:
|
||||
# l.lot_quantity -= nw
|
||||
# if l.lot_final_quantity:
|
||||
# l.lot_final_quantity -= gw
|
||||
# if l.lot_tu_net_qt:
|
||||
# l.lot_tu_net_qt -= nwtu
|
||||
# if l.lot_tu_gross_qt:
|
||||
# l.lot_tu_gross_qt -= gwtu
|
||||
# if l.lot_wr_net_qt:
|
||||
# l.lot_wr_net_qt -= nwwr
|
||||
# if l.lot_wr_gross_qt:
|
||||
# l.lot_wr_gross_qt -= gwwr
|
||||
# if l.lot_lr_net_qt:
|
||||
# l.lot_lr_net_qt -= nwlr
|
||||
# if l.lot_lr_gross_qt:
|
||||
# l.lot_lr_gross_qt -= gwlr
|
||||
# values['lot_quantity'] = nw
|
||||
# values['lot_final_quantity'] = gw
|
||||
# values['lot_tu_net_qt'] = nwtu
|
||||
# values['lot_tu_gross_qt'] = gwtu
|
||||
# values['lot_wr_net_qt'] = nwwr
|
||||
# values['lot_wr_gross_qt'] = gwwr
|
||||
# values['lot_lr_net_qt'] = nwlr
|
||||
# values['lot_lr_gross_qt'] = gwlr
|
||||
# L.save([l])
|
||||
|
||||
# return super(Lot, cls).create(vlist)
|
||||
|
||||
# @classmethod
|
||||
# def validate(cls, lots):
|
||||
# super(Lot, cls).validate(lots)
|
||||
# pool = Pool()
|
||||
# Valuation = pool.get('valuation.valuation')
|
||||
# Position = pool.get('trade.position')
|
||||
# for lot in lots:
|
||||
# L = pool.get('position.lot')
|
||||
# if not lot.lot_himself:
|
||||
# l = L(lot.id)
|
||||
# l.lot_himself = lot.id
|
||||
# L.save([l])
|
||||
# if lot.lot_parent:
|
||||
# pa = L(lot.lot_parent)
|
||||
# l = L(lot.id)
|
||||
# l.position = pa.position
|
||||
# l.lot_quality = pa.lot_quality
|
||||
# l.lot_unit = pa.lot_unit
|
||||
# L.save([l])
|
||||
|
||||
# @classmethod
|
||||
# def copy(cls, positions, default=None):
|
||||
# if default is None:
|
||||
# default = {}
|
||||
# else:
|
||||
# default = default.copy()
|
||||
# return super(Lot, cls).copy(lots, default=default)
|
||||
|
||||
# @classmethod
|
||||
# def delete(cls, lots):
|
||||
# positions = []
|
||||
# pool = Pool()
|
||||
# LL = pool.get('position.lot')
|
||||
# for lot in lots:
|
||||
# positions.append(lot.position)
|
||||
# if lot.lot_parent:
|
||||
# pa = LL(lot.lot_parent)
|
||||
# if pa:
|
||||
# pa.lot_bale += lot.lot_bale
|
||||
# pa.lot_quantity += lot.lot_quantity
|
||||
# pa.lot_final_quantity += lot.lot_final_quantity
|
||||
# if pa.lot_tu_net_qt:
|
||||
# pa.lot_tu_net_qt += lot.lot_tu_net_qt
|
||||
# pa.lot_tu_gross_qt += lot.lot_tu_gross_qt
|
||||
# if pa.lot_wr_net_qt:
|
||||
# pa.lot_wr_net_qt += lot.lot_wr_net_qt
|
||||
# pa.lot_wr_gross_qt += lot.lot_wr_gross_qt
|
||||
# if pa.lot_lr_net_qt:
|
||||
# pa.lot_lr_net_qt += lot.lot_lr_net_qt
|
||||
# pa.lot_lr_gross_qt += lot.lot_lr_gross_qt
|
||||
# LL.save([pa])
|
||||
# super(Lot, cls).delete(lots)
|
||||
|
||||
class QtType(ModelSQL, ModelView):
|
||||
"Type"
|
||||
__name__ = 'lot.qt.type'
|
||||
name = fields.Char("Name")
|
||||
|
||||
class QtHist(ModelSQL, ModelView):
|
||||
"Quantities"
|
||||
__name__ = 'lot.qt'
|
||||
|
||||
lot = fields.Many2One(
|
||||
'lot.lot', "Lot", ondelete='CASCADE',
|
||||
)
|
||||
quantity_type = fields.Many2One('lot.qt.type',"Type")
|
||||
quantity = fields.Numeric("Quantity")
|
||||
|
||||
|
||||
|
||||
57
modules/lot/build/lib/trytond/modules/lot/lot.xml
Executable file
57
modules/lot/build/lib/trytond/modules/lot/lot.xml
Executable file
@@ -0,0 +1,57 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="lot_view_tree">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence2">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence2</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence3">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence3</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_form">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">lot_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_view_tree">
|
||||
<field name="model">lot.qt</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_qt_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_view_tree_sequence">
|
||||
<field name="model">lot.qt</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_qt_tree_sequence</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_type_view_tree">
|
||||
<field name="model">lot.qt.type</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_qt_type_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_type_view_tree_sequence">
|
||||
<field name="model">lot.qt.type</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_qt_type_tree_sequence</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
9
modules/lot/build/lib/trytond/modules/lot/tryton.cfg
Executable file
9
modules/lot/build/lib/trytond/modules/lot/tryton.cfg
Executable file
@@ -0,0 +1,9 @@
|
||||
[tryton]
|
||||
version=7.2.7
|
||||
depends:
|
||||
ir
|
||||
purchase
|
||||
sale
|
||||
res
|
||||
xml:
|
||||
lot.xml
|
||||
26
modules/lot/build/lib/trytond/modules/lot/view/lot_form.xml
Executable file
26
modules/lot/build/lib/trytond/modules/lot/view/lot_form.xml
Executable file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot_name"/>
|
||||
<field name="lot_name"/>
|
||||
<label name="lot_parent"/>
|
||||
<field name="lot_parent"/>
|
||||
<label name="lot_qt"/>
|
||||
<field name="lot_qt"/>
|
||||
<label name="lot_product"/>
|
||||
<field name="lot_product"/>
|
||||
<label name="lot_type"/>
|
||||
<field name="lot_type"/>
|
||||
<label name="lot_status"/>
|
||||
<field name="lot_status"/>
|
||||
<label name="lot_shipment"/>
|
||||
<field name="lot_shipment"/>
|
||||
<label name="lot_quantity"/>
|
||||
<field name="lot_quantity"/>
|
||||
<label name="lot_gross_quantity"/>
|
||||
<field name="lot_gross_quantity"/>
|
||||
<newline/>
|
||||
<label name="lot_unit"/>
|
||||
<field name="lot_unit"/>
|
||||
<newline/>
|
||||
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="purchase.lot_view_tree_sequence2,purchase.lot_view_form"/>
|
||||
</form>
|
||||
4
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_tree.xml
Executable file
4
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_tree.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<tree>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
</tree>
|
||||
4
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_tree_sequence.xml
Executable file
4
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_tree_sequence.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<tree>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
</tree>
|
||||
3
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_type_tree.xml
Executable file
3
modules/lot/build/lib/trytond/modules/lot/view/lot_qt_type_tree.xml
Executable file
@@ -0,0 +1,3 @@
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,3 @@
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
12
modules/lot/build/lib/trytond/modules/lot/view/lot_tree.xml
Executable file
12
modules/lot/build/lib/trytond/modules/lot/view/lot_tree.xml
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree>
|
||||
<field name="lot_himself"/>
|
||||
<field name="line"/>
|
||||
<field name="lot_type"/>
|
||||
<field name="lot_status"/>
|
||||
<field name="lot_shipment"/>
|
||||
<field name="lot_quantity"/>
|
||||
<field name="lot_unit"/>
|
||||
<field name="lot_gross_quantity"/>
|
||||
<field name="lot_parent"/>
|
||||
</tree>
|
||||
16
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence.xml
Executable file
16
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence.xml
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="lot_himself" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_product" width="80"/>
|
||||
<field name="lot_type" width="80" optional="1"/>
|
||||
<field name="lot_status" width="80" optional="1"/>
|
||||
<field name="lot_shipment" width="120"/>
|
||||
<field name="lot_quantity" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_gross_quantity" width="80"/>
|
||||
<field name="lot_premium" width="80"/>
|
||||
<field name="lot_price" width="80"/>
|
||||
<field name="lot_type" width="80"/>
|
||||
<field name="lot_status" width="80"/>
|
||||
</tree>
|
||||
8
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence2.xml
Executable file
8
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence2.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="lot_name" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_quantity" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_gross_quantity" width="80"/>
|
||||
</tree>
|
||||
14
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence3.xml
Executable file
14
modules/lot/build/lib/trytond/modules/lot/view/lot_tree_sequence3.xml
Executable file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="lot_himself" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_product" width="80"/>
|
||||
<field name="lot_type" width="80" optional="1"/>
|
||||
<field name="lot_status" width="80" optional="1"/>
|
||||
<field name="lot_shipment" width="120"/>
|
||||
<field name="lot_quantity" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_gross_quantity" width="80"/>
|
||||
<field name="lot_premium_sale" width="80"/>
|
||||
<field name="lot_price_sale" width="80"/>
|
||||
</tree>
|
||||
BIN
modules/lot/dist/trytond_lot-7.2.7-py3.11.egg
vendored
Executable file
BIN
modules/lot/dist/trytond_lot-7.2.7-py3.11.egg
vendored
Executable file
Binary file not shown.
805
modules/lot/lot.py
Executable file
805
modules/lot/lot.py
Executable file
@@ -0,0 +1,805 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Bool, Eval, Id
|
||||
from trytond.model import (ModelSQL, ModelView)
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
from trytond.wizard import Button, StateTransition, StateView, Wizard, StateAction
|
||||
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
|
||||
from sql import Column, Literal
|
||||
from sql.functions import CurrentTimestamp, DateTrunc
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Lot(ModelSQL, ModelView):
|
||||
"Lot"
|
||||
__name__ = 'lot.lot'
|
||||
_rec_name = 'lot_name'
|
||||
|
||||
lot_name = fields.Char("Lot")
|
||||
number = fields.Char("Number", readonly=True)
|
||||
lot_qt = fields.Float("Quantity",required=False)
|
||||
lot_unit = fields.Many2One('product.uom', "Unit",required=False)
|
||||
lot_product = fields.Many2One('product.product', "Product")
|
||||
lot_type = fields.Selection([
|
||||
('virtual', 'Open'),
|
||||
('physic', 'Physic'),
|
||||
('loss', 'Loss'),
|
||||
], 'Qt type')
|
||||
lot_status = fields.Selection([
|
||||
('forecast', 'Forecast'),
|
||||
('loading', 'Loading'),
|
||||
('transit', 'Transit'),
|
||||
('destination', 'Destination'),
|
||||
('stock', 'Stock'),
|
||||
('delivered', 'Delivered')
|
||||
], 'Where')
|
||||
lot_av = fields.Selection([
|
||||
('available', 'Available'),
|
||||
('reserved', 'Reserved'),
|
||||
('locked', 'Locked'),
|
||||
('prov', 'Prov. inv'),
|
||||
('invoiced', 'Invoiced')
|
||||
], 'State')
|
||||
lot_quantity = fields.Function(fields.Numeric("Net weight",digits='line_unit'),'get_current_quantity')
|
||||
lot_premium = fields.Numeric("Premium", digits='line_unit')
|
||||
lot_premium_sup = fields.Numeric("Sup prem", digits='line_unit')
|
||||
lot_premium_tpl = fields.Numeric("Tpl prem", digits='line_unit')
|
||||
lot_premium_sale = fields.Numeric("Prem/Disc", digits='line_unit')
|
||||
lot_shipment_in = fields.Many2One('stock.shipment.in', "Shipment In")
|
||||
lot_shipment_out = fields.Many2One('stock.shipment.out', "Shipment Out")
|
||||
lot_shipment_internal = fields.Many2One('stock.shipment.internal', "Shipment Internal")
|
||||
lot_gross_quantity = fields.Function(fields.Numeric("Gross weight",digits='line_unit'),'get_current_gross_quantity')
|
||||
lot_parent = fields.Many2One('lot.lot',"Parent")
|
||||
lot_childs = fields.One2Many('lot.lot','lot_parent',"Childs")
|
||||
lot_himself = fields.Many2One('lot.lot',"Lot")
|
||||
lot_container = fields.Char("Container")
|
||||
lot_unit_line = fields.Many2One('product.uom', "Unit",required=True)
|
||||
lot_price = fields.Function(fields.Numeric("Price", digits='line_unit'),'get_lot_price')
|
||||
lot_price_sale = fields.Function(fields.Numeric("Price", digits='line_unit'),'get_lot_sale_price')
|
||||
lot_state = fields.Many2One('lot.qt.type',"Qt state")
|
||||
lot_hist = fields.One2Many('lot.qt.hist','lot',"Qt hist")
|
||||
lot_pur_inv_state = fields.Selection([
|
||||
(None, ''),
|
||||
('prov', 'Prov'),
|
||||
('invoiced', 'Final')
|
||||
], 'Pur. inv')
|
||||
lot_sale_inv_state = fields.Selection([
|
||||
(None, ''),
|
||||
('prov', 'Prov'),
|
||||
('invoiced', 'Final')
|
||||
], 'Sale inv')
|
||||
lot_price_ct_symbol = fields.Function(fields.Char(""),'get_price_ct_symbol')
|
||||
lot_price_ct_symbol_sale = fields.Function(fields.Char(""),'get_price_ct_symbol_sale')
|
||||
lot_price_ct_symbol_premium = fields.Function(fields.Char(""),'get_price_ct_symbol_premium')
|
||||
line_unit = fields.Function(fields.Many2One('product.uom',"Line unit"),'get_unit_line')
|
||||
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",
|
||||
)
|
||||
|
||||
lot_role = fields.Selection([
|
||||
('normal', 'Normal'),
|
||||
('technical', 'Technical'),
|
||||
], 'Role', required=True)
|
||||
|
||||
split_operations = fields.One2Many(
|
||||
'lot.split.merge', 'source_lot', 'Split/Merge Ops'
|
||||
)
|
||||
|
||||
split_graph = fields.Function(
|
||||
fields.Text('Split Graph'),
|
||||
'get_split_graph'
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
def default_lot_role(cls):
|
||||
return 'normal'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Technical helpers
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
def _check_split_allowed(self):
|
||||
if self.lot_role != 'normal':
|
||||
raise UserError('Only normal lots can be split.')
|
||||
if self.lot_type != 'physic':
|
||||
raise UserError('Only physical lots can be split.')
|
||||
if self.IsDelivered():
|
||||
raise UserError('Delivered lots cannot be split.')
|
||||
|
||||
def _create_technical_parent(self):
|
||||
Lot = Pool().get('lot.lot')
|
||||
parent = Lot(
|
||||
lot_name=f'TECH-{self.lot_name}',
|
||||
lot_role='technical',
|
||||
lot_type='virtual',
|
||||
lot_product=self.lot_product,
|
||||
lot_status=self.lot_status,
|
||||
lot_av='locked',
|
||||
)
|
||||
Lot.save([parent])
|
||||
return parent
|
||||
|
||||
def _clone_hist_with_ratio(self, ratio):
|
||||
LotQtHist = Pool().get('lot.qt.hist')
|
||||
hist = []
|
||||
for h in self.lot_hist:
|
||||
hist.append(
|
||||
LotQtHist(
|
||||
quantity_type=h.quantity_type,
|
||||
quantity=h.quantity * ratio,
|
||||
gross_quantity=h.gross_quantity * ratio,
|
||||
)
|
||||
)
|
||||
return hist
|
||||
|
||||
def split_by_weight(self, splits):
|
||||
Date = Pool().get('ir.date')
|
||||
self._check_split_allowed()
|
||||
|
||||
total_weight = sum(s['quantity'] for s in splits)
|
||||
lot_weight = self.get_current_quantity()
|
||||
|
||||
diff = (lot_weight - total_weight).quantize(Decimal('0.00001'))
|
||||
|
||||
if diff < 0:
|
||||
raise UserError('Split weight exceeds lot weight.')
|
||||
|
||||
if diff != 0:
|
||||
raise UserError(
|
||||
f'Split does not fully consume the lot weight.\n'
|
||||
f'Remaining: {diff}'
|
||||
)
|
||||
|
||||
Lot = Pool().get('lot.lot')
|
||||
Split = Pool().get('lot.split.merge')
|
||||
|
||||
children = []
|
||||
# Parent becomes technical
|
||||
self.lot_av = 'locked'
|
||||
self.lot_role = 'technical'
|
||||
self.lot_name = 'TECH-' + self.lot_name
|
||||
line = self.line
|
||||
self.line = None
|
||||
Lot.save([self])
|
||||
|
||||
for s in splits:
|
||||
ratio = s['quantity'] / lot_weight
|
||||
|
||||
lot_qt = None
|
||||
if self.lot_qt:
|
||||
lot_qt = (Decimal(self.lot_qt) * ratio).quantize(
|
||||
Decimal('0.00001'), rounding=ROUND_HALF_UP
|
||||
)
|
||||
|
||||
child = Lot(
|
||||
lot_name=s.get('name'),
|
||||
lot_role='normal',
|
||||
lot_type='physic',
|
||||
lot_qt=float(lot_qt) if lot_qt is not None else None,
|
||||
lot_unit=self.lot_unit,
|
||||
lot_product=self.lot_product,
|
||||
lot_status=self.lot_status,
|
||||
lot_av=self.lot_av,
|
||||
lot_unit_line=self.lot_unit_line,
|
||||
)
|
||||
|
||||
with Transaction().set_context(_skip_function_fields=True):
|
||||
Lot.save([child])
|
||||
child.set_current_quantity(s['quantity'], s['quantity'], 1)
|
||||
child.lot_parent = self
|
||||
child.line = line
|
||||
Lot.save([child])
|
||||
|
||||
children.append(child)
|
||||
|
||||
ops = []
|
||||
for c in children:
|
||||
ops.append(
|
||||
Split(
|
||||
operation='split',
|
||||
source_lot=self,
|
||||
target_lot=c,
|
||||
lot_qt=c.lot_qt,
|
||||
quantity=c.get_current_quantity(),
|
||||
operation_date=Date.today(),
|
||||
)
|
||||
)
|
||||
Split.save(ops)
|
||||
|
||||
return children
|
||||
|
||||
@classmethod
|
||||
def merge_lots(cls, lots):
|
||||
Date = Pool().get('ir.date')
|
||||
if len(lots) < 2:
|
||||
raise UserError('At least two lots are required for merge.')
|
||||
|
||||
# parent = lots[0].lot_parent
|
||||
# if not parent or parent.lot_role != 'technical':
|
||||
# raise UserError('Lots must share a technical parent.')
|
||||
|
||||
product = lots[0].lot_product
|
||||
for l in lots:
|
||||
if l.lot_product != product:
|
||||
raise UserError('Cannot merge lots of different products.')
|
||||
|
||||
Lot = Pool().get('lot.lot')
|
||||
Split = Pool().get('lot.split.merge')
|
||||
|
||||
total_qt = sum([l.lot_qt or 0 for l in lots])
|
||||
total_weight = sum([l.get_current_quantity() for l in lots])
|
||||
|
||||
ops = []
|
||||
parents_name = ""
|
||||
for l in lots:
|
||||
l.lot_av = 'locked'
|
||||
l.lot_role = 'technical'
|
||||
l.line = None
|
||||
parents_name += l.lot_name + "/"
|
||||
ops.append(
|
||||
Split(
|
||||
operation='merge',
|
||||
source_lot=l,
|
||||
target_lot=merged,
|
||||
lot_qt=l.lot_qt,
|
||||
quantity=l.get_current_quantity(),
|
||||
operation_date=Date.today(),
|
||||
)
|
||||
)
|
||||
|
||||
Lot.save(lots)
|
||||
Split.save(ops)
|
||||
|
||||
merged = Lot(
|
||||
lot_name='MERGE-' + parents_name,
|
||||
lot_role='normal',
|
||||
lot_type='physic',
|
||||
lot_product=product,
|
||||
lot_qt=total_qt,
|
||||
lot_unit=lots[0].lot_unit,
|
||||
lot_unit_line=lots[0].lot_unit_line,
|
||||
)
|
||||
|
||||
merged.set_current_quantity(total_weight, total_weight, 1)
|
||||
Lot.save([merged])
|
||||
|
||||
return merged
|
||||
|
||||
def get_split_graph(self, name=None):
|
||||
Split = Pool().get('lot.split.merge')
|
||||
|
||||
nodes = {}
|
||||
edges = []
|
||||
|
||||
def add_lot_node(lot):
|
||||
if lot.id in nodes:
|
||||
return
|
||||
nodes[lot.id] = {
|
||||
'id': f'lot.lot {lot.id}',
|
||||
'label': f'{lot.lot_name}\n({lot.lot_role})',
|
||||
'data_model': 'lot.lot',
|
||||
'data_id': lot.id,
|
||||
'shape': 'box' if lot.lot_role == 'technical' else 'ellipse',
|
||||
'color': {
|
||||
'background': '#FFD966' if lot.lot_role == 'technical' else '#A7C7E7',
|
||||
'border': '#555',
|
||||
},
|
||||
}
|
||||
|
||||
def walk(lot):
|
||||
add_lot_node(lot)
|
||||
ops = Split.search([('source_lot', '=', lot.id)])
|
||||
for op in ops:
|
||||
tgt = op.target_lot
|
||||
add_lot_node(tgt)
|
||||
edges.append({
|
||||
'from': f'lot.lot {lot.id}',
|
||||
'to': f'lot.lot {tgt.id}',
|
||||
'label': op.operation if op.operation else '',
|
||||
'arrows': 'to',
|
||||
'color': {
|
||||
'color': '#267F82',
|
||||
'highlight': '#1D5F62',
|
||||
'hover': '#1D5F62'
|
||||
}
|
||||
})
|
||||
walk(tgt)
|
||||
|
||||
walk(self)
|
||||
|
||||
# Calcul des relations parent-enfant
|
||||
from collections import defaultdict
|
||||
|
||||
children_map = defaultdict(list)
|
||||
parent_map = {}
|
||||
|
||||
for edge in edges:
|
||||
from_id = edge['from']
|
||||
to_id = edge['to']
|
||||
children_map[from_id].append(to_id)
|
||||
parent_map[to_id] = from_id
|
||||
|
||||
# Trouver la racine (nœud sans parent)
|
||||
all_node_ids = {node['id'] for node in nodes.values()}
|
||||
root_candidates = all_node_ids - set(parent_map.keys())
|
||||
|
||||
if root_candidates:
|
||||
root_id = list(root_candidates)[0]
|
||||
else:
|
||||
root_id = f'lot.lot {self.id}'
|
||||
|
||||
# Assigner les niveaux hiérarchiques
|
||||
levels = {}
|
||||
|
||||
def assign_levels(node_id, level=0):
|
||||
if node_id in levels and levels[node_id] >= level:
|
||||
return
|
||||
levels[node_id] = level
|
||||
for child in children_map.get(node_id, []):
|
||||
assign_levels(child, level + 1)
|
||||
|
||||
assign_levels(root_id)
|
||||
|
||||
# Grouper les nœuds par niveau
|
||||
by_level = defaultdict(list)
|
||||
for node_id, level in levels.items():
|
||||
by_level[level].append(node_id)
|
||||
|
||||
# Calculer les positions - IMPORTANT: utiliser des valeurs positives plus grandes
|
||||
# Calculer les positions pour un graphe HORIZONTAL
|
||||
X_SPACING = 180 # espace entre les niveaux (gauche → droite)
|
||||
Y_SPACING = 100 # espace entre les nœuds d’un même niveau
|
||||
|
||||
max_nodes_in_level = max(len(nodes) for nodes in by_level.values()) if by_level else 1
|
||||
total_height = max_nodes_in_level * Y_SPACING
|
||||
|
||||
for level, node_ids in by_level.items():
|
||||
level_height = len(node_ids) * Y_SPACING
|
||||
start_y = (total_height - level_height) / 2 # Centrer verticalement
|
||||
|
||||
for i, node_id in enumerate(node_ids):
|
||||
node_num = int(node_id.split()[-1])
|
||||
|
||||
# Niveau → X
|
||||
x_pos = level * X_SPACING + 300
|
||||
|
||||
# Répartition verticale
|
||||
y_pos = start_y + (i * Y_SPACING) + 100
|
||||
|
||||
nodes[node_num]['x'] = x_pos
|
||||
nodes[node_num]['y'] = y_pos
|
||||
nodes[node_num]['fixed'] = {
|
||||
'x': True,
|
||||
'y': True
|
||||
}
|
||||
|
||||
|
||||
# S'assurer que tous les nœuds ont des positions
|
||||
for node in nodes.values():
|
||||
if 'x' not in node:
|
||||
node['x'] = 0
|
||||
node['y'] = 0
|
||||
node['fixed'] = {'x': True, 'y': True}
|
||||
|
||||
data = {
|
||||
'nodes': list(nodes.values()),
|
||||
'edges': edges,
|
||||
'physics': {
|
||||
'enabled': False # Désactivé car positions fixes
|
||||
}
|
||||
}
|
||||
|
||||
return 'visjson:' + json.dumps(data)
|
||||
|
||||
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:
|
||||
return 'stock.shipment.internal,' + str(self.lot_shipment_internal.id)
|
||||
return None
|
||||
|
||||
def get_unit_line(self,name):
|
||||
if self.line:
|
||||
return self.line.unit
|
||||
return self.lot_unit_line
|
||||
def get_lot_inv(self):
|
||||
if self.invoice_line:
|
||||
return self.invoice_line.invoice.origin
|
||||
if self.invoice_line_prov:
|
||||
return self.invoice_line_prov.invoice.origin
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Lot, cls).__setup__()
|
||||
#cls._order.insert(0, ('lot_shipment', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def default_lot_qt(cls):
|
||||
return Decimal(0)
|
||||
|
||||
@classmethod
|
||||
def default_lot_av(cls):
|
||||
return 'available'
|
||||
|
||||
@classmethod
|
||||
def default_lot_status(cls):
|
||||
return 'forecast'
|
||||
|
||||
@classmethod
|
||||
def default_lot_type(cls):
|
||||
return 'physic'
|
||||
|
||||
@classmethod
|
||||
def default_lot_gross_quantity(cls):
|
||||
return Decimal(0)
|
||||
|
||||
@classmethod
|
||||
def default_lot_type(cls):
|
||||
return 'receive'
|
||||
|
||||
@classmethod
|
||||
def default_lot_state(cls):
|
||||
LotQtType = Pool().get('lot.qt.type')
|
||||
lqt = LotQtType.search([('sequence','=',1)])
|
||||
if lqt:
|
||||
return lqt[0].id
|
||||
|
||||
def get_price_ct_symbol(self,name):
|
||||
if self.line:
|
||||
return str(self.line.currency.symbol) + "/" + str(self.line.unit.symbol)
|
||||
|
||||
def get_price_ct_symbol_sale(self,name):
|
||||
if self.sale_line:
|
||||
return str(self.sale_line.currency.symbol) + "/" + str(self.sale_line.unit.symbol)
|
||||
|
||||
def get_price_ct_symbol_premium(self,name):
|
||||
if self.line:
|
||||
if self.line.enable_linked_currency:
|
||||
return str(self.line.linked_currency.name) + "/" + str(self.line.linked_unit.symbol)
|
||||
else:
|
||||
return str(self.line.currency.symbol) + "/" + str(self.line.unit.symbol)
|
||||
|
||||
def get_hist_quantity(self,seq):
|
||||
qt = Decimal(0)
|
||||
gross_qt = Decimal(0)
|
||||
if self.lot_state:
|
||||
if self.lot_hist:
|
||||
if seq != 0:
|
||||
st = seq
|
||||
else:
|
||||
st = self.lot_state.id
|
||||
lot = [e for e in self.lot_hist if e.quantity_type.id == st][0]
|
||||
qt = round(lot.quantity,5)
|
||||
gross_qt = round(lot.gross_quantity,5)
|
||||
return qt, gross_qt
|
||||
|
||||
def get_virtual_diff(self):
|
||||
Uom = Pool().get('product.uom')
|
||||
line = self.line if self.line else self.sale_line
|
||||
if line:
|
||||
if line.lots:
|
||||
physic_sum = Decimal(0)
|
||||
for l in line.lots:
|
||||
if l.lot_type == 'physic' :
|
||||
physic_sum += round(Decimal(Uom.compute_qty(Uom(l.lot_unit_line),float(l.get_current_quantity()),l.line.unit)),5)
|
||||
return line.quantity_theorical - physic_sum
|
||||
|
||||
def get_current_quantity(self,name=None):
|
||||
# if self.lot_type == 'physic':
|
||||
qt, gross_qt = self.get_hist_quantity(0)
|
||||
return qt
|
||||
# else:
|
||||
# return self.get_virtual_diff()
|
||||
|
||||
def get_current_quantity_converted(self,name=None):
|
||||
Uom = Pool().get('product.uom')
|
||||
unit = self.line.unit if self.line else self.sale_line.unit
|
||||
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(self.get_current_quantity()), unit)),5)
|
||||
|
||||
def get_current_gross_quantity(self,name=None):
|
||||
if self.lot_type == 'physic':
|
||||
qt, gross_qt = self.get_hist_quantity(0)
|
||||
return gross_qt
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_quantity_to_hist(self,net,gross,qt_type):
|
||||
LotQtHist = Pool().get('lot.qt.hist')
|
||||
lqh = LotQtHist()
|
||||
lqh.quantity_type = qt_type
|
||||
lqh.quantity = net
|
||||
lqh.gross_quantity = gross
|
||||
lqh.lot = self
|
||||
return lqh
|
||||
|
||||
def set_current_quantity(self, net, gross, seq=1):
|
||||
LotQtType = Pool().get('lot.qt.type')
|
||||
lqtt = LotQtType.search([('sequence', '=', seq)])
|
||||
if not lqtt:
|
||||
return
|
||||
|
||||
lot_hist = list(getattr(self, 'lot_hist', []) or [])
|
||||
existing = [e for e in lot_hist if e.quantity_type == lqtt[0]]
|
||||
|
||||
if existing:
|
||||
hist = existing[0]
|
||||
hist.quantity = net
|
||||
hist.gross_quantity = gross
|
||||
else:
|
||||
lot_hist.append(self.add_quantity_to_hist(net, gross, lqtt[0]))
|
||||
|
||||
self.lot_hist = lot_hist
|
||||
self.lot_state = lqtt[0]
|
||||
|
||||
def get_unit(self,name):
|
||||
if self.line:
|
||||
return self.line.unit
|
||||
if self.sale_line:
|
||||
return self.sale_line.unit
|
||||
|
||||
def get_lot_price(self,name=None):
|
||||
price = Decimal(0)
|
||||
if self.line:
|
||||
if self.line.enable_linked_currency and self.line.linked_price and self.line.linked_currency and self.line.price_type == 'priced':
|
||||
return self.line.get_price_linked_currency((self.lot_premium if self.lot_premium else 0))
|
||||
else:
|
||||
return self.line.get_price((self.lot_premium if self.lot_premium else 0))
|
||||
|
||||
def get_lot_sale_price(self,name=None):
|
||||
price = Decimal(0)
|
||||
if self.sale_line:
|
||||
# if self.line.enable_linked_currency and self.line.linked_price and self.line.linked_currency and self.line.price_type == 'priced':
|
||||
# return self.line.get_price_linked_currency((self.lot_premium if self.lot_premium else 0))
|
||||
# else:
|
||||
return self.sale_line.get_price((self.lot_premium if self.lot_premium else 0))
|
||||
|
||||
def get_sale_amount(self,name):
|
||||
round_context = getcontext()
|
||||
round_context.rounding = ROUND_HALF_UP
|
||||
price = self.get_lot_sale_price()
|
||||
return None
|
||||
|
||||
def get_amount(self,name):
|
||||
round_context = getcontext()
|
||||
round_context.rounding = ROUND_HALF_UP
|
||||
price = self.get_lot_price()
|
||||
return None
|
||||
|
||||
class QtType(ModelSQL, ModelView):
|
||||
"Type"
|
||||
__name__ = 'lot.qt.type'
|
||||
name = fields.Char("Name")
|
||||
sequence = fields.Integer("Sequence")
|
||||
|
||||
class QtHist(ModelSQL, ModelView):
|
||||
"Quantities"
|
||||
__name__ = 'lot.qt.hist'
|
||||
|
||||
lot = fields.Many2One(
|
||||
'lot.lot', "Lot", ondelete='CASCADE',
|
||||
)
|
||||
quantity_type = fields.Many2One('lot.qt.type',"Type")
|
||||
quantity = fields.Numeric("Net weight",digits=(1,5))
|
||||
gross_quantity = fields.Numeric("Gross weight",digits=(1,5))
|
||||
|
||||
class LotSplitMerge(ModelSQL, ModelView):
|
||||
"Lot Split Merge"
|
||||
__name__ = 'lot.split.merge'
|
||||
|
||||
operation = fields.Selection([
|
||||
('split', 'Split'),
|
||||
('merge', 'Merge'),
|
||||
], 'Operation', required=True)
|
||||
|
||||
source_lot = fields.Many2One(
|
||||
'lot.lot', 'Source Lot', required=True, ondelete='CASCADE'
|
||||
)
|
||||
target_lot = fields.Many2One(
|
||||
'lot.lot', 'Target Lot', required=True, ondelete='CASCADE'
|
||||
)
|
||||
|
||||
lot_qt = fields.Float('Elements count')
|
||||
quantity = fields.Numeric('Weight', digits=(16, 5))
|
||||
|
||||
operation_date = fields.Date('Date', required=True)
|
||||
reversed_by = fields.Many2One(
|
||||
'lot.split.merge', 'Reversed By'
|
||||
)
|
||||
|
||||
class SplitLine(ModelView):
|
||||
"Split Line"
|
||||
__name__ = 'lot.split.wizard.line'
|
||||
|
||||
name = fields.Char('Lot name')
|
||||
lot_qt = fields.Float('Quantity')
|
||||
weight = fields.Numeric('Weight', digits=(16,5))
|
||||
|
||||
class SplitWizardStart(ModelView):
|
||||
__name__ = 'lot.split.wizard.start'
|
||||
|
||||
mode = fields.Selection([
|
||||
('equal', 'Equal parts'),
|
||||
('manual', 'Manual'),
|
||||
], 'Mode', required=True)
|
||||
|
||||
parts = fields.Integer(
|
||||
'Parts',
|
||||
states={'required': Eval('mode') == 'equal'}
|
||||
)
|
||||
|
||||
create_remainder = fields.Boolean(
|
||||
'Create remainder lot',
|
||||
help='If checked, a remainder lot will be created when the split '
|
||||
'does not exactly match the original weight.'
|
||||
)
|
||||
|
||||
lines = fields.One2Many(
|
||||
'lot.split.wizard.line',
|
||||
None,
|
||||
'Lines',
|
||||
states={'invisible': Eval('mode') != 'manual'}
|
||||
)
|
||||
|
||||
class SplitWizard(Wizard):
|
||||
"Lot Split Wizard"
|
||||
__name__ = 'lot.split.wizard'
|
||||
|
||||
start = StateView(
|
||||
'lot.split.wizard.start',
|
||||
'lot.split_wizard_start_view',
|
||||
[
|
||||
Button('Cancel', 'end', 'tryton-cancel'),
|
||||
Button('Split', 'split', 'tryton-ok', default=True),
|
||||
]
|
||||
)
|
||||
|
||||
split = StateTransition()
|
||||
def transition_split(self):
|
||||
Lot = Pool().get('lot.lot')
|
||||
lot = Lot(Transaction().context['active_id'])
|
||||
|
||||
if self.start.mode == 'equal':
|
||||
part = lot.get_current_quantity() / self.start.parts
|
||||
splits = [
|
||||
{'quantity': part, 'name': f'{lot.lot_name}-{i+1}'}
|
||||
for i in range(self.start.parts)
|
||||
]
|
||||
else:
|
||||
splits = [
|
||||
{'quantity': l.weight, 'lot_qt': l.lot_qt, 'name': l.name}
|
||||
for l in self.start.lines
|
||||
]
|
||||
|
||||
lot.split_by_weight(splits)
|
||||
return 'end'
|
||||
|
||||
class SplitWizard(Wizard):
|
||||
__name__ = 'lot.split.wizard'
|
||||
|
||||
start = StateView(
|
||||
'lot.split.wizard.start',
|
||||
'lot.split_wizard_start_view',
|
||||
[
|
||||
Button('Cancel', 'end', 'tryton-cancel'),
|
||||
Button('Split', 'split', 'tryton-ok', default=True),
|
||||
]
|
||||
)
|
||||
|
||||
split = StateTransition()
|
||||
|
||||
def transition_split(self):
|
||||
Lot = Pool().get('lot.lot')
|
||||
lot = Lot(Transaction().context['active_id'])
|
||||
|
||||
total_weight = lot.get_current_quantity().quantize(
|
||||
Decimal('0.00001'), rounding=ROUND_HALF_UP
|
||||
)
|
||||
|
||||
splits = []
|
||||
allocated = Decimal('0.0')
|
||||
|
||||
# -------------------------------------------------
|
||||
# EQUAL MODE
|
||||
# -------------------------------------------------
|
||||
if self.start.mode == 'equal':
|
||||
if not self.start.parts or self.start.parts <= 0:
|
||||
raise UserError('Number of parts must be greater than zero.')
|
||||
|
||||
base = (total_weight / self.start.parts).quantize(
|
||||
Decimal('0.00001'), rounding=ROUND_HALF_UP
|
||||
)
|
||||
|
||||
for i in range(self.start.parts):
|
||||
qty = base
|
||||
if i == self.start.parts - 1:
|
||||
qty = total_weight - allocated # absorbe le reste
|
||||
|
||||
splits.append({
|
||||
'quantity': qty,
|
||||
'lot_qt': lot.lot_qt / self.start.parts if lot.lot_qt else None,
|
||||
'name': f'{lot.lot_name}-{i + 1}',
|
||||
})
|
||||
allocated += qty
|
||||
|
||||
# -------------------------------------------------
|
||||
# MANUAL MODE
|
||||
# -------------------------------------------------
|
||||
else:
|
||||
for line in self.start.lines:
|
||||
if not line.weight or line.weight <= 0:
|
||||
raise UserError('Each line must have a positive weight.')
|
||||
|
||||
splits.append({
|
||||
'quantity': line.weight,
|
||||
'lot_qt': line.lot_qt,
|
||||
'name': line.name,
|
||||
})
|
||||
allocated += line.weight
|
||||
|
||||
diff = (total_weight - allocated).quantize(
|
||||
Decimal('0.00001'), rounding=ROUND_HALF_UP
|
||||
)
|
||||
|
||||
if diff != 0:
|
||||
if self.start.create_remainder and diff > 0:
|
||||
splits.append({
|
||||
'quantity': diff,
|
||||
'lot_qt': None,
|
||||
'name': f'{lot.lot_name}-REMAINDER',
|
||||
})
|
||||
else:
|
||||
raise UserError(
|
||||
f'Split does not match the lot weight.\n'
|
||||
f'Remaining difference: {diff}'
|
||||
)
|
||||
|
||||
lot.split_by_weight(splits)
|
||||
return 'end'
|
||||
|
||||
class MergeLotsStart(ModelView):
|
||||
"Merge Lots Start"
|
||||
__name__ = 'lot.merge.wizard.start'
|
||||
|
||||
confirm = fields.Boolean('Confirm merge')
|
||||
|
||||
|
||||
class MergeLots(Wizard):
|
||||
"Merge Lots"
|
||||
__name__ = 'lot.merge.wizard'
|
||||
|
||||
start = StateView(
|
||||
'lot.merge.wizard.start',
|
||||
'lot.lot_merge_wizard_start_view',
|
||||
[
|
||||
Button('Cancel', 'end', 'tryton-cancel'),
|
||||
Button('Merge', 'merge', 'tryton-ok', default=True),
|
||||
]
|
||||
)
|
||||
|
||||
merge = StateTransition()
|
||||
|
||||
def transition_merge(self):
|
||||
Lot = Pool().get('lot.lot')
|
||||
ids = Transaction().context['active_ids']
|
||||
lots = Lot.browse(ids)
|
||||
|
||||
Lot.merge_lots(lots)
|
||||
return 'end'
|
||||
145
modules/lot/lot.xml
Executable file
145
modules/lot/lot.xml
Executable file
@@ -0,0 +1,145 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.sequence.type" id="sequence_type_lot">
|
||||
<field name="name">Lot</field>
|
||||
</record>
|
||||
<record model="ir.sequence" id="lot_sequence">
|
||||
<field name="name">Lot sequence</field>
|
||||
<field name="sequence_type" ref="sequence_type_lot"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_graph">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_graph</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_graph2">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_graph2</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_graph3">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_graph3</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence2">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence2</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_tree_sequence4">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_tree_sequence4</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_view_form">
|
||||
<field name="model">lot.lot</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">lot_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_view_tree">
|
||||
<field name="model">lot.qt</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_qt_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_view_tree_sequence">
|
||||
<field name="model">lot.qt</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_qt_tree_sequence</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_type_view_tree">
|
||||
<field name="model">lot.qt.type</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_qt_type_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_type_view_tree_sequence">
|
||||
<field name="model">lot.qt.type</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_qt_type_tree_sequence</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_hist_view_form">
|
||||
<field name="model">lot.qt.hist</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">lot_qt_hist_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_hist_view_tree">
|
||||
<field name="model">lot.qt.hist</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">lot_qt_hist_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="lot_qt_hist_view_tree_sequence">
|
||||
<field name="model">lot.qt.hist</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">lot_qt_hist_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="split_merge_tree_view">
|
||||
<field name="model">lot.split.merge</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">split_merge_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="split_line_tree_view">
|
||||
<field name="model">lot.split.wizard.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">split_line_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="split_wizard_start_view">
|
||||
<field name="model">lot.split.wizard.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">split_start</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="action_split_wizard">
|
||||
<field name="name">Split/Merge Lot</field>
|
||||
<field name="wiz_name">lot.split.wizard</field>
|
||||
<field name="model">lot.lot</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_split_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">lot.lot,-1</field>
|
||||
<field name="action" ref="action_split_wizard"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="lot_merge_wizard_start_view">
|
||||
<field name="model">lot.merge.wizard.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">merge_start</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="action_merge_lots">
|
||||
<field name="name">📦 Merge Lots</field>
|
||||
<field name="wiz_name">lot.merge.wizard</field>
|
||||
<field name="model">lot.report</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_merge_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">lot.report,-1</field>
|
||||
<field name="action" ref="action_merge_lots"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
123
modules/lot/setup.py
Executable file
123
modules/lot/setup.py
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from configparser import ConfigParser
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
|
||||
def read(fname):
|
||||
return io.open(
|
||||
os.path.join(os.path.dirname(__file__), fname),
|
||||
'r', encoding='utf-8').read()
|
||||
|
||||
|
||||
def get_require_version(name):
|
||||
require = '%s >= %s.%s, < %s.%s'
|
||||
require %= (name, major_version, minor_version,
|
||||
major_version, minor_version + 1)
|
||||
return require
|
||||
|
||||
|
||||
config = ConfigParser()
|
||||
config.read_file(open(os.path.join(os.path.dirname(__file__), 'tryton.cfg')))
|
||||
info = dict(config.items('tryton'))
|
||||
for key in ('depends', 'extras_depend', 'xml'):
|
||||
if key in info:
|
||||
info[key] = info[key].strip().splitlines()
|
||||
version = info.get('version', '0.0.1')
|
||||
major_version, minor_version, _ = version.split('.', 2)
|
||||
major_version = int(major_version)
|
||||
minor_version = int(minor_version)
|
||||
name = 'trytond_lot'
|
||||
|
||||
if minor_version % 2:
|
||||
download_url = ''
|
||||
else:
|
||||
download_url = 'http://downloads.tryton.org/%s.%s/' % (
|
||||
major_version, minor_version)
|
||||
|
||||
requires = []
|
||||
for dep in info.get('depends', []):
|
||||
if not re.match(r'(ir|res)(\W|$)', dep):
|
||||
requires.append(get_require_version('trytond_%s' % dep))
|
||||
requires.append(get_require_version('trytond'))
|
||||
|
||||
setup(name=name,
|
||||
version=version,
|
||||
description='Tryton module to add period to product',
|
||||
author='Tryton',
|
||||
author_email='foundation@tryton.org',
|
||||
url='http://www.tryton.org/',
|
||||
download_url=download_url,
|
||||
project_urls={
|
||||
"Bug Tracker": 'https://bugs.tryton.org/',
|
||||
"Documentation": 'https://docs.tryton.org/',
|
||||
"Forum": 'https://www.tryton.org/forum',
|
||||
"Source Code": 'https://code.tryton.org/tryton',
|
||||
},
|
||||
keywords='tryton lot',
|
||||
package_dir={'trytond.modules.lot': '.'},
|
||||
packages=(
|
||||
['trytond.modules.lot']
|
||||
+ ['trytond.modules.lot.%s' % p
|
||||
for p in find_packages()]
|
||||
),
|
||||
package_data={
|
||||
'trytond.modules.lot': (info.get('xml', [])
|
||||
+ ['tryton.cfg', 'view/*.xml', 'locale/*.po']),
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Plugins',
|
||||
'Framework :: Tryton',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Financial and Insurance Industry',
|
||||
'Intended Audience :: Legal Industry',
|
||||
'Intended Audience :: Manufacturing',
|
||||
'License :: OSI Approved :: '
|
||||
'GNU General Public License v3 or later (GPLv3+)',
|
||||
'Natural Language :: Bulgarian',
|
||||
'Natural Language :: Catalan',
|
||||
'Natural Language :: Chinese (Simplified)',
|
||||
'Natural Language :: Czech',
|
||||
'Natural Language :: Dutch',
|
||||
'Natural Language :: English',
|
||||
'Natural Language :: Finnish',
|
||||
'Natural Language :: French',
|
||||
'Natural Language :: German',
|
||||
'Natural Language :: Hungarian',
|
||||
'Natural Language :: Indonesian',
|
||||
'Natural Language :: Italian',
|
||||
'Natural Language :: Persian',
|
||||
'Natural Language :: Polish',
|
||||
'Natural Language :: Portuguese (Brazilian)',
|
||||
'Natural Language :: Romanian',
|
||||
'Natural Language :: Russian',
|
||||
'Natural Language :: Slovenian',
|
||||
'Natural Language :: Spanish',
|
||||
'Natural Language :: Turkish',
|
||||
'Natural Language :: Ukrainian',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Topic :: Office/Business',
|
||||
],
|
||||
license='GPL-3',
|
||||
python_requires='>=3.8',
|
||||
install_requires=requires,
|
||||
zip_safe=False,
|
||||
entry_points="""
|
||||
[trytond.modules]
|
||||
lot = trytond.modules.lot
|
||||
""",
|
||||
)
|
||||
10
modules/lot/tryton.cfg
Executable file
10
modules/lot/tryton.cfg
Executable file
@@ -0,0 +1,10 @@
|
||||
[tryton]
|
||||
version=7.2.7
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
product
|
||||
stock
|
||||
price
|
||||
xml:
|
||||
lot.xml
|
||||
53
modules/lot/trytond_lot.egg-info/PKG-INFO
Executable file
53
modules/lot/trytond_lot.egg-info/PKG-INFO
Executable file
@@ -0,0 +1,53 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: trytond-lot
|
||||
Version: 7.2.7
|
||||
Summary: Tryton module to add period to product
|
||||
Home-page: http://www.tryton.org/
|
||||
Download-URL: http://downloads.tryton.org/7.2/
|
||||
Author: Tryton
|
||||
Author-email: foundation@tryton.org
|
||||
License: GPL-3
|
||||
Project-URL: Bug Tracker, https://bugs.tryton.org/
|
||||
Project-URL: Documentation, https://docs.tryton.org/
|
||||
Project-URL: Forum, https://www.tryton.org/forum
|
||||
Project-URL: Source Code, https://code.tryton.org/tryton
|
||||
Keywords: tryton lot
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Plugins
|
||||
Classifier: Framework :: Tryton
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Intended Audience :: Financial and Insurance Industry
|
||||
Classifier: Intended Audience :: Legal Industry
|
||||
Classifier: Intended Audience :: Manufacturing
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Classifier: Natural Language :: Bulgarian
|
||||
Classifier: Natural Language :: Catalan
|
||||
Classifier: Natural Language :: Chinese (Simplified)
|
||||
Classifier: Natural Language :: Czech
|
||||
Classifier: Natural Language :: Dutch
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: Natural Language :: Finnish
|
||||
Classifier: Natural Language :: French
|
||||
Classifier: Natural Language :: German
|
||||
Classifier: Natural Language :: Hungarian
|
||||
Classifier: Natural Language :: Indonesian
|
||||
Classifier: Natural Language :: Italian
|
||||
Classifier: Natural Language :: Persian
|
||||
Classifier: Natural Language :: Polish
|
||||
Classifier: Natural Language :: Portuguese (Brazilian)
|
||||
Classifier: Natural Language :: Romanian
|
||||
Classifier: Natural Language :: Russian
|
||||
Classifier: Natural Language :: Slovenian
|
||||
Classifier: Natural Language :: Spanish
|
||||
Classifier: Natural Language :: Turkish
|
||||
Classifier: Natural Language :: Ukrainian
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Topic :: Office/Business
|
||||
Requires-Python: >=3.8
|
||||
21
modules/lot/trytond_lot.egg-info/SOURCES.txt
Executable file
21
modules/lot/trytond_lot.egg-info/SOURCES.txt
Executable file
@@ -0,0 +1,21 @@
|
||||
setup.py
|
||||
./__init__.py
|
||||
./lot.py
|
||||
./lot.xml
|
||||
./tryton.cfg
|
||||
./view/lot_form.xml
|
||||
./view/lot_qt_tree.xml
|
||||
./view/lot_qt_tree_sequence.xml
|
||||
./view/lot_qt_type_tree.xml
|
||||
./view/lot_qt_type_tree_sequence.xml
|
||||
./view/lot_tree.xml
|
||||
./view/lot_tree_sequence.xml
|
||||
./view/lot_tree_sequence2.xml
|
||||
./view/lot_tree_sequence3.xml
|
||||
trytond_lot.egg-info/PKG-INFO
|
||||
trytond_lot.egg-info/SOURCES.txt
|
||||
trytond_lot.egg-info/dependency_links.txt
|
||||
trytond_lot.egg-info/entry_points.txt
|
||||
trytond_lot.egg-info/not-zip-safe
|
||||
trytond_lot.egg-info/requires.txt
|
||||
trytond_lot.egg-info/top_level.txt
|
||||
1
modules/lot/trytond_lot.egg-info/dependency_links.txt
Executable file
1
modules/lot/trytond_lot.egg-info/dependency_links.txt
Executable file
@@ -0,0 +1 @@
|
||||
|
||||
2
modules/lot/trytond_lot.egg-info/entry_points.txt
Executable file
2
modules/lot/trytond_lot.egg-info/entry_points.txt
Executable file
@@ -0,0 +1,2 @@
|
||||
[trytond.modules]
|
||||
lot = trytond.modules.lot
|
||||
1
modules/lot/trytond_lot.egg-info/not-zip-safe
Executable file
1
modules/lot/trytond_lot.egg-info/not-zip-safe
Executable file
@@ -0,0 +1 @@
|
||||
|
||||
3
modules/lot/trytond_lot.egg-info/requires.txt
Executable file
3
modules/lot/trytond_lot.egg-info/requires.txt
Executable file
@@ -0,0 +1,3 @@
|
||||
trytond<7.3,>=7.2
|
||||
trytond_purchase<7.3,>=7.2
|
||||
trytond_sale<7.3,>=7.2
|
||||
1
modules/lot/trytond_lot.egg-info/top_level.txt
Executable file
1
modules/lot/trytond_lot.egg-info/top_level.txt
Executable file
@@ -0,0 +1 @@
|
||||
trytond
|
||||
49
modules/lot/view/lot_form.xml
Executable file
49
modules/lot/view/lot_form.xml
Executable file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot_name"/>
|
||||
<field name="lot_name"/>
|
||||
<label name="lot_parent"/>
|
||||
<field name="lot_parent"/>
|
||||
<label name="lot_qt"/>
|
||||
<field name="lot_qt"/>
|
||||
<label name="lot_unit"/>
|
||||
<field name="lot_unit"/>
|
||||
<newline/>
|
||||
<label name="lot_product"/>
|
||||
<field name="lot_product"/>
|
||||
<label name="lot_type"/>
|
||||
<field name="lot_type"/>
|
||||
<label name="lot_status"/>
|
||||
<field name="lot_status"/>
|
||||
<label name="lot_state"/>
|
||||
<field name="lot_state"/>
|
||||
<newline/>
|
||||
<label name="lot_quantity"/>
|
||||
<field name="lot_quantity"/>
|
||||
<label name="lot_gross_quantity"/>
|
||||
<field name="lot_gross_quantity"/>
|
||||
<newline/>
|
||||
<label name="lot_unit_line"/>
|
||||
<field name="lot_unit_line"/>
|
||||
<label name="lot_premium"/>
|
||||
<field name="lot_premium"/>
|
||||
<newline/>
|
||||
<label name="warrant_nb"/>
|
||||
<field name="warrant_nb"/>
|
||||
<label name="lot_role"/>
|
||||
<field name="lot_role"/>
|
||||
<newline/>
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="lot.lot_view_tree_sequence2,lot.lot_view_form"/>
|
||||
<field name="lot_hist" colspan="4"/>
|
||||
</page>
|
||||
<page string="Accounting" id="accounting">
|
||||
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
||||
</page>
|
||||
<page string="Split/Merge" id="split">
|
||||
<field name="split_operations" colspan="4" mode="tree" view_ids="lot.split_merge_tree_view"/>
|
||||
<field name="split_graph" widget="html_viewer" colspan="4"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
12
modules/lot/view/lot_graph.xml
Executable file
12
modules/lot/view/lot_graph.xml
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<graph type="pie">
|
||||
<x>
|
||||
<field name="lot_product"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="lot_price" string="USD Pprice"/>
|
||||
<field name="lot_quantity" string="Quantity sold Mt"/>
|
||||
</y>
|
||||
</graph>
|
||||
12
modules/lot/view/lot_graph2.xml
Executable file
12
modules/lot/view/lot_graph2.xml
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<graph type="vbar">
|
||||
<x>
|
||||
<field name="lot_product"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="lot_price" string="USD Price"/>
|
||||
<field name="lot_quantity" string="Quantity sold Mt"/>
|
||||
</y>
|
||||
</graph>
|
||||
12
modules/lot/view/lot_graph3.xml
Executable file
12
modules/lot/view/lot_graph3.xml
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<graph type="line">
|
||||
<x>
|
||||
<field name="lot_product"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="lot_price" string="USD Price"/>
|
||||
<field name="lot_quantity" string="Quantity sold Mt"/>
|
||||
</y>
|
||||
</graph>
|
||||
11
modules/lot/view/lot_qt_hist_form.xml
Executable file
11
modules/lot/view/lot_qt_hist_form.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot"/>
|
||||
<field name="lot"/>
|
||||
<label name="quantity_type"/>
|
||||
<field name="quantity_type"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="gross_quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
</form>
|
||||
6
modules/lot/view/lot_qt_hist_tree.xml
Executable file
6
modules/lot/view/lot_qt_hist_tree.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<tree editable="1">
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
</tree>
|
||||
6
modules/lot/view/lot_qt_hist_tree_sequence.xml
Executable file
6
modules/lot/view/lot_qt_hist_tree_sequence.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<tree editable="1">
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
</tree>
|
||||
4
modules/lot/view/lot_qt_tree.xml
Executable file
4
modules/lot/view/lot_qt_tree.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<tree>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
</tree>
|
||||
8
modules/lot/view/lot_qt_tree_sequence.xml
Executable file
8
modules/lot/view/lot_qt_tree_sequence.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<tree>
|
||||
<field name="lot_p"/>
|
||||
<field name="lot_quantity"/>
|
||||
<field name="lot_unit"/>
|
||||
<field name="lot_av"/>
|
||||
<field name="lot_status"/>
|
||||
<field name="lot_s"/>
|
||||
</tree>
|
||||
4
modules/lot/view/lot_qt_type_tree.xml
Executable file
4
modules/lot/view/lot_qt_type_tree.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="sequence"/>
|
||||
</tree>
|
||||
4
modules/lot/view/lot_qt_type_tree_sequence.xml
Executable file
4
modules/lot/view/lot_qt_type_tree_sequence.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="sequence"/>
|
||||
</tree>
|
||||
12
modules/lot/view/lot_tree.xml
Executable file
12
modules/lot/view/lot_tree.xml
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree>
|
||||
<field name="id"/>
|
||||
<field name="line"/>
|
||||
<field name="sale_line"/>
|
||||
<field name="lot_type"/>
|
||||
<field name="lot_status"/>
|
||||
<field name="lot_quantity"/>
|
||||
<field name="lot_unit"/>
|
||||
<field name="lot_gross_quantity"/>
|
||||
<field name="lot_parent"/>
|
||||
</tree>
|
||||
19
modules/lot/view/lot_tree_sequence.xml
Executable file
19
modules/lot/view/lot_tree_sequence.xml
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="0">
|
||||
<field name="lot_name" />
|
||||
<field name="lot_type" />
|
||||
<field name="lot_qt"/>
|
||||
<field name="lot_unit"/>
|
||||
<field name="lot_product"/>
|
||||
<field name="lot_shipment_origin"/>
|
||||
<field name="lot_quantity"/>
|
||||
<field name="lot_gross_quantity"/>
|
||||
<field name="lot_unit_line"/>
|
||||
<field name="lot_premium" width="80"/>
|
||||
<field name="lot_premium_sup" width="80"/>
|
||||
<field name="lot_price_ct_symbol_premium"/>
|
||||
<field name="lot_price"/>
|
||||
<field name="lot_price_ct_symbol"/>
|
||||
<field name="lot_type" width="80" optional="1"/>
|
||||
<field name="lot_status" width="80" optional="1"/>
|
||||
</tree>
|
||||
8
modules/lot/view/lot_tree_sequence2.xml
Executable file
8
modules/lot/view/lot_tree_sequence2.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="id" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_quantity" symbol="r_lot_unit_line" width="80"/>
|
||||
<field name="lot_gross_quantity" symbol="r_lot_unit_line" width="80"/>
|
||||
</tree>
|
||||
13
modules/lot/view/lot_tree_sequence3.xml
Executable file
13
modules/lot/view/lot_tree_sequence3.xml
Executable file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="lot_himself" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_product" width="80"/>
|
||||
<field name="lot_type" width="80" optional="1"/>
|
||||
<field name="lot_status" width="80" optional="1"/>
|
||||
<field name="lot_quantity" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_gross_quantity" width="80"/>
|
||||
<field name="lot_premium_sale" width="80"/>
|
||||
<field name="lot_price_sale" width="80"/>
|
||||
</tree>
|
||||
8
modules/lot/view/lot_tree_sequence4.xml
Executable file
8
modules/lot/view/lot_tree_sequence4.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<tree sequence="sequence" editable="0">
|
||||
<field name="lot_name" width="80"/>
|
||||
<field name="lot_qt" width="80"/>
|
||||
<field name="lot_unit" width="80"/>
|
||||
<field name="lot_quantity_sale" symbol="r_lot_unit_line" width="80"/>
|
||||
<field name="lot_gross_quantity_sale" symbol="r_lot_unit_line" width="80"/>
|
||||
</tree>
|
||||
4
modules/lot/view/merge_start.xml
Normal file
4
modules/lot/view/merge_start.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<form>
|
||||
<label name="confirm"/>
|
||||
<field name="confirm"/>
|
||||
</form>
|
||||
5
modules/lot/view/split_line_tree.xml
Normal file
5
modules/lot/view/split_line_tree.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="lot_qt"/>
|
||||
<field name="weight"/>
|
||||
</tree>
|
||||
7
modules/lot/view/split_merge_tree.xml
Normal file
7
modules/lot/view/split_merge_tree.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<tree>
|
||||
<field name="operation"/>
|
||||
<field name="target_lot"/>
|
||||
<field name="lot_qt"/>
|
||||
<field name="quantity"/>
|
||||
<field name="operation_date"/>
|
||||
</tree>
|
||||
9
modules/lot/view/split_start.xml
Normal file
9
modules/lot/view/split_start.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<form col="2">
|
||||
<label name="mode"/>
|
||||
<field name="mode"/>
|
||||
<label name="parts"/>
|
||||
<field name="parts"/>
|
||||
<label name="create_remainder"/>
|
||||
<field name="create_remainder"/>
|
||||
<field name="lines" colspan="2"/>
|
||||
</form>
|
||||
Reference in New Issue
Block a user