first commit
This commit is contained in:
2
modules/stock_consignment/__init__.py
Normal file
2
modules/stock_consignment/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
BIN
modules/stock_consignment/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/stock_consignment/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/stock_consignment/__pycache__/account.cpython-311.pyc
Normal file
BIN
modules/stock_consignment/__pycache__/account.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/stock_consignment/__pycache__/stock.cpython-311.pyc
Normal file
BIN
modules/stock_consignment/__pycache__/stock.cpython-311.pyc
Normal file
Binary file not shown.
22
modules/stock_consignment/account.py
Normal file
22
modules/stock_consignment/account.py
Normal 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, PoolMeta
|
||||
|
||||
|
||||
class InvoiceLine(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
name = super().origin_name
|
||||
if (isinstance(self.origin, Move)
|
||||
and self.origin.id >= 0
|
||||
and self.origin.shipment):
|
||||
name = self.origin.shipment.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + ['stock.move']
|
||||
15
modules/stock_consignment/locale/bg.po
Normal file
15
modules/stock_consignment/locale/bg.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
17
modules/stock_consignment/locale/ca.po
Normal file
17
modules/stock_consignment/locale/ca.po
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Tercer consignació"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Línies de factura de consignació"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
"El tercer al que es facturarà quan es s'utilitzin els moviments de "
|
||||
"consignació."
|
||||
15
modules/stock_consignment/locale/cs.po
Normal file
15
modules/stock_consignment/locale/cs.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
17
modules/stock_consignment/locale/de.po
Normal file
17
modules/stock_consignment/locale/de.po
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Konsignant"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Konsignationsrechnungsposition"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
"Die Partei an die verrechnet wird wenn das Konsignationslager verwendet "
|
||||
"wird."
|
||||
16
modules/stock_consignment/locale/es.po
Normal file
16
modules/stock_consignment/locale/es.po
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Tercero consignación"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Lineas de factura de consignación"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
"El tercero a facturar cuando se utiliza los movimientos de consignación."
|
||||
15
modules/stock_consignment/locale/es_419.po
Normal file
15
modules/stock_consignment/locale/es_419.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/et.po
Normal file
15
modules/stock_consignment/locale/et.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Saadetise osapool"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "Arve saaja kui kasutatakse partiiladu."
|
||||
15
modules/stock_consignment/locale/fa.po
Normal file
15
modules/stock_consignment/locale/fa.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "موجودی انبار نهاد/سازمان"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "هنگامی که موجودی انبار استفاده شود، نهاد/سازمان صورتحساب میسازد."
|
||||
15
modules/stock_consignment/locale/fi.po
Normal file
15
modules/stock_consignment/locale/fi.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/fr.po
Normal file
15
modules/stock_consignment/locale/fr.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Tiers de consignation"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Lignes de facture de consignation"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "Le tiers facturé quand le stock de consignation est utilisé."
|
||||
15
modules/stock_consignment/locale/hu.po
Normal file
15
modules/stock_consignment/locale/hu.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/id.po
Normal file
15
modules/stock_consignment/locale/id.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/it.po
Normal file
15
modules/stock_consignment/locale/it.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Controparte Spedizione"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Righe di fattura di conto vendita"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "La controparte da fatturare quando si utilizza il deposito merce."
|
||||
15
modules/stock_consignment/locale/lo.po
Normal file
15
modules/stock_consignment/locale/lo.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/lt.po
Normal file
15
modules/stock_consignment/locale/lt.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
17
modules/stock_consignment/locale/nl.po
Normal file
17
modules/stock_consignment/locale/nl.po
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Consignatie Relatie"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Consignatie factuur regels"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
"De relatie waar aan wordt gefactureerd wanneer de consignatie voorraad wordt"
|
||||
" gebruikt."
|
||||
15
modules/stock_consignment/locale/pl.po
Normal file
15
modules/stock_consignment/locale/pl.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/pt.po
Normal file
15
modules/stock_consignment/locale/pt.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Consignação da Pessoa"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "A pessoa a faturar quando se utiliza a movimentação de consignação."
|
||||
15
modules/stock_consignment/locale/ro.po
Normal file
15
modules/stock_consignment/locale/ro.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Partea de consignație"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr "Rânduri de factură de consignație"
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "Partea facturată atunci când se utilizează stocul în consignație."
|
||||
15
modules/stock_consignment/locale/ru.po
Normal file
15
modules/stock_consignment/locale/ru.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/sl.po
Normal file
15
modules/stock_consignment/locale/sl.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Kosignacijski partner"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "V primeru kosignacijske zaloge se zaračuna kosignacijskemu partnerju."
|
||||
15
modules/stock_consignment/locale/tr.po
Normal file
15
modules/stock_consignment/locale/tr.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr "Gönderilen Mal Partisi"
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr "Gönderilen mal stoğu kullanıldığı zaman faturalandırılan parti."
|
||||
15
modules/stock_consignment/locale/uk.po
Normal file
15
modules/stock_consignment/locale/uk.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
15
modules/stock_consignment/locale/zh_CN.po
Normal file
15
modules/stock_consignment/locale/zh_CN.po
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:stock.location,consignment_party:"
|
||||
msgid "Consignment Party"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "field:stock.move,consignment_invoice_lines:"
|
||||
msgid "Consignment Invoice Lines"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "help:stock.location,consignment_party:"
|
||||
msgid "The party invoiced when consignment stock is used."
|
||||
msgstr ""
|
||||
398
modules/stock_consignment/stock.py
Normal file
398
modules/stock_consignment/stock.py
Normal file
@@ -0,0 +1,398 @@
|
||||
# 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 functools import wraps
|
||||
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
|
||||
|
||||
class Location(metaclass=PoolMeta):
|
||||
__name__ = 'stock.location'
|
||||
|
||||
consignment_party = fields.Many2One(
|
||||
'party.party', "Consignment Party",
|
||||
states={
|
||||
'invisible': ~Eval('type').in_(['supplier', 'storage']),
|
||||
},
|
||||
help="The party invoiced when consignment stock is used.")
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.lost_found_location.states['invisible'] &= (
|
||||
~Eval('type').in_(['supplier', 'customer']))
|
||||
|
||||
@classmethod
|
||||
def _parent_domain(cls):
|
||||
domain = super()._parent_domain()
|
||||
domain['supplier'].append('storage')
|
||||
domain['storage'].append('customer')
|
||||
return domain
|
||||
|
||||
@property
|
||||
def lost_found_used(self):
|
||||
lost_found = super().lost_found_used
|
||||
if not lost_found and not self.warehouse and self.type == 'storage':
|
||||
location = self.parent
|
||||
while location:
|
||||
if location.type in {'supplier', 'storage'}:
|
||||
lost_found = location.lost_found_location
|
||||
break
|
||||
location = location.parent
|
||||
return lost_found
|
||||
|
||||
|
||||
class LocationLeadTime(metaclass=PoolMeta):
|
||||
__name__ = 'stock.location.lead_time'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.warehouse_to.domain = ['OR',
|
||||
cls.warehouse_to.domain,
|
||||
('type', '=', 'storage'),
|
||||
]
|
||||
|
||||
|
||||
def set_origin_consignment(state):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, moves):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
to_save = []
|
||||
move2line = {}
|
||||
for move in moves:
|
||||
if not move.consignment_invoice_lines:
|
||||
lines = move.get_invoice_lines_consignment()
|
||||
if lines:
|
||||
to_save.extend(lines)
|
||||
move2line[move] = lines[0]
|
||||
if to_save:
|
||||
InvoiceLine.save(to_save)
|
||||
for move, line in move2line.items():
|
||||
if not move.origin:
|
||||
move.origin = line
|
||||
original_state, move.state = move.state, state
|
||||
if (move.on_change_with_unit_price_required()
|
||||
and move.unit_price is None):
|
||||
move.unit_price = line.unit_price
|
||||
move.currency = line.currency
|
||||
else:
|
||||
move.unit_price = None
|
||||
move.currency = None
|
||||
move.state = original_state
|
||||
cls.save(list(move2line.keys()))
|
||||
return func(cls, moves)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def unset_origin_consignment(state):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, moves):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
lines, to_save = [], set()
|
||||
for move in moves:
|
||||
for invoice_line in move.consignment_invoice_lines:
|
||||
lines.append(invoice_line)
|
||||
if move.origin == move:
|
||||
move.origin = None
|
||||
to_save.add(move)
|
||||
if (not move.on_change_with_unit_price_required()
|
||||
and (move.unit_price or move.currency)):
|
||||
move.unit_price = None
|
||||
move.currency = None
|
||||
to_save.add(move)
|
||||
if lines:
|
||||
InvoiceLine.delete(lines)
|
||||
if to_save:
|
||||
cls.save(list(to_save))
|
||||
return func(cls, moves)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
|
||||
consignment_invoice_lines = fields.One2Many(
|
||||
'account.invoice.line', 'origin', "Consignment Invoice Lines",
|
||||
readonly=True,
|
||||
states={
|
||||
'invisible': ~Eval('consignment_invoice_lines'),
|
||||
})
|
||||
|
||||
@fields.depends(
|
||||
'state', 'from_location', 'to_location', 'unit_price', 'currency')
|
||||
def on_change_with_unit_price_required(self, name=None):
|
||||
required = super().on_change_with_unit_price_required(name)
|
||||
if (required
|
||||
and self.state in {'staging', 'draft'}
|
||||
and self.from_location
|
||||
and self.to_location
|
||||
and ((
|
||||
self.from_location.type == 'supplier'
|
||||
and self.to_location.type in {
|
||||
'storage', 'production', 'customer'})
|
||||
or (self.from_location.type in {
|
||||
'storage', 'production', 'supplier'}
|
||||
and self.to_location.type == 'customer'))
|
||||
and self.from_location.consignment_party):
|
||||
# Keep the requirement to allow origin consignment decorators to
|
||||
# set the unit price before changing the move state
|
||||
required = (self.unit_price is not None) and self.currency
|
||||
return required
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + ['account.invoice.line']
|
||||
|
||||
@fields.depends('from_location')
|
||||
def on_change_with_assignation_required(self, name=None):
|
||||
required = super().on_change_with_assignation_required(
|
||||
name=name)
|
||||
if self.from_location:
|
||||
if (self.quantity
|
||||
and self.from_location.type == 'supplier'
|
||||
and self.from_location.warehouse):
|
||||
required = True
|
||||
return required
|
||||
|
||||
@classmethod
|
||||
def search_assignation_required(cls, name, clause):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
_, operator, operand = clause
|
||||
domain = super().search_assignation_required(name, clause)
|
||||
reverse = {
|
||||
'=': '!=',
|
||||
'!=': '=',
|
||||
}
|
||||
if operator in reverse:
|
||||
with inactive_records():
|
||||
warehouses = Location.search([
|
||||
('type', '=', 'warehouse'),
|
||||
])
|
||||
warehouse_ids = list(map(int, warehouses))
|
||||
if operator == '!=':
|
||||
bool_op = 'AND'
|
||||
warehouse_op = 'not child_of'
|
||||
else:
|
||||
bool_op = 'OR'
|
||||
warehouse_op = 'child_of'
|
||||
if not operand:
|
||||
operator = reverse[operator]
|
||||
domain = [bool_op,
|
||||
domain,
|
||||
[('quantity', '!=', 0),
|
||||
('from_location.type', operator, 'supplier'),
|
||||
('from_location', warehouse_op, warehouse_ids, 'parent'),
|
||||
],
|
||||
]
|
||||
return domain
|
||||
|
||||
@property
|
||||
def is_supplier_consignment(self):
|
||||
return (self.from_location.type == 'supplier'
|
||||
and self.to_location.type in {'storage', 'production', 'customer'}
|
||||
and self.from_location.consignment_party)
|
||||
|
||||
@property
|
||||
def is_customer_consignment(self):
|
||||
return (
|
||||
self.from_location.type in {'storage', 'production', 'supplier'}
|
||||
and self.to_location.type == 'customer'
|
||||
and self.from_location.consignment_party)
|
||||
|
||||
def get_invoice_lines_consignment(self):
|
||||
lines = []
|
||||
if self.is_supplier_consignment:
|
||||
lines.append(self._get_supplier_invoice_line_consignment())
|
||||
if self.is_customer_consignment:
|
||||
lines.append(self._get_customer_invoice_line_consignment())
|
||||
return lines
|
||||
|
||||
def _get_supplier_invoice_line_consignment(self):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
Product = pool.get('product.product')
|
||||
ProductSupplier = pool.get('purchase.product_supplier')
|
||||
|
||||
with Transaction().set_context(
|
||||
supplier=self.from_location.consignment_party.id):
|
||||
pattern = ProductSupplier.get_pattern()
|
||||
for product_supplier in self.product.product_suppliers_used(**pattern):
|
||||
currency = product_supplier.currency
|
||||
break
|
||||
else:
|
||||
currency = self.company.currency
|
||||
|
||||
line = InvoiceLine()
|
||||
line.invoice_type = 'in'
|
||||
line.type = 'line'
|
||||
line.company = self.company
|
||||
line.party = self.from_location.consignment_party
|
||||
line.currency = currency
|
||||
line.product = self.product
|
||||
line.quantity = self.quantity
|
||||
line.unit = self.unit
|
||||
line.stock_moves = [self]
|
||||
line.origin = self
|
||||
line.on_change_product()
|
||||
|
||||
with Transaction().set_context(
|
||||
currency=line.currency.id,
|
||||
supplier=line.party.id,
|
||||
uom=line.unit.id,
|
||||
taxes=[t.id for t in line.taxes]):
|
||||
line.unit_price = Product.get_purchase_price(
|
||||
[line.product], line.quantity)[line.product.id]
|
||||
return line
|
||||
|
||||
def _get_customer_invoice_line_consignment(self):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
Product = pool.get('product.product')
|
||||
|
||||
line = InvoiceLine()
|
||||
line.invoice_type = 'out'
|
||||
line.type = 'line'
|
||||
line.company = self.company
|
||||
line.party = self.from_location.consignment_party
|
||||
line.currency = self.company.currency
|
||||
line.product = self.product
|
||||
line.quantity = self.quantity
|
||||
line.unit = self.unit
|
||||
line.stock_moves = [self]
|
||||
line.origin = self
|
||||
line.on_change_product()
|
||||
|
||||
with Transaction().set_context(
|
||||
currency=line.currency.id,
|
||||
customer=line.party.id,
|
||||
uom=line.unit.id,
|
||||
taxes=[t.id for t in line.taxes]):
|
||||
line.unit_price = Product.get_sale_price(
|
||||
[line.product], line.quantity)[line.product.id]
|
||||
return line
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
@unset_origin_consignment('draft')
|
||||
def draft(cls, moves):
|
||||
super().draft(moves)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('assigned')
|
||||
@set_origin_consignment('assigned')
|
||||
def assign(cls, moves):
|
||||
super().assign(moves)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@set_origin_consignment('done')
|
||||
def do(cls, moves):
|
||||
super().do(moves)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@unset_origin_consignment('cancelled')
|
||||
def cancel(cls, moves):
|
||||
super().cancel(moves)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, moves, default=None):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
|
||||
default = default.copy() if default is not None else {}
|
||||
consigment_moves = {
|
||||
m.id for m in moves if isinstance(m.origin, InvoiceLine)}
|
||||
|
||||
def consigment_moves_cleared(name, default):
|
||||
default = default.copy()
|
||||
|
||||
def default_value(data):
|
||||
if data['id'] in consigment_moves:
|
||||
return None
|
||||
elif name in default:
|
||||
if callable(default[name]):
|
||||
return default[name](data)
|
||||
else:
|
||||
return default[name]
|
||||
else:
|
||||
return data.get(name)
|
||||
return default_value
|
||||
|
||||
default['origin'] = consigment_moves_cleared('origin', default)
|
||||
default['unit_price'] = consigment_moves_cleared('unit_price', default)
|
||||
default['currency'] = consigment_moves_cleared('currency', default)
|
||||
|
||||
moves = super().copy(moves, default=default)
|
||||
if not Transaction().context.get('_stock_move_split'):
|
||||
to_save = []
|
||||
for move in moves:
|
||||
if isinstance(move.origin, InvoiceLine):
|
||||
move.origin = None
|
||||
to_save.append(move)
|
||||
if to_save:
|
||||
cls.save(to_save)
|
||||
return moves
|
||||
|
||||
|
||||
class ShipmentInternal(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.internal'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.from_location.domain = ['OR',
|
||||
cls.from_location.domain,
|
||||
('type', '=', 'supplier'),
|
||||
]
|
||||
cls.to_location.domain = ['OR',
|
||||
cls.to_location.domain,
|
||||
('type', 'in', ['supplier', 'customer']),
|
||||
]
|
||||
|
||||
@fields.depends('to_location')
|
||||
def on_change_with_planned_start_date(self, pattern=None):
|
||||
if pattern is None:
|
||||
pattern = {}
|
||||
if self.to_location and not self.to_location.warehouse:
|
||||
pattern.setdefault('location_to', self.to_location.id)
|
||||
return super().on_change_with_planned_start_date(
|
||||
pattern=pattern)
|
||||
|
||||
|
||||
class Inventory(metaclass=PoolMeta):
|
||||
__name__ = 'stock.inventory'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.location.domain = ['OR',
|
||||
cls.location.domain,
|
||||
('type', '=', 'supplier'),
|
||||
]
|
||||
|
||||
|
||||
class OrderPoint(metaclass=PoolMeta):
|
||||
__name__ = 'stock.order_point'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.provisioning_location.domain = ['OR',
|
||||
cls.provisioning_location.domain,
|
||||
('type', '=', 'supplier'),
|
||||
]
|
||||
18
modules/stock_consignment/stock.xml
Normal file
18
modules/stock_consignment/stock.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="location_view_form">
|
||||
<field name="model">stock.location</field>
|
||||
<field name="inherit" ref="stock.location_view_form"/>
|
||||
<field name="name">location_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="stock_move_view_form">
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit" ref="stock.move_view_form"/>
|
||||
<field name="name">stock_move_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
2
modules/stock_consignment/tests/__init__.py
Normal file
2
modules/stock_consignment/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
266
modules/stock_consignment/tests/scenario_stock_consignment.rst
Normal file
266
modules/stock_consignment/tests/scenario_stock_consignment.rst
Normal file
@@ -0,0 +1,266 @@
|
||||
==========================
|
||||
Stock Consignment Scenario
|
||||
==========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_tax, get_accounts)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_consignment', create_company, create_chart)
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> supplier_tax = create_tax(Decimal('.10'))
|
||||
>>> supplier_tax.save()
|
||||
>>> customer_tax = create_tax(Decimal('.10'))
|
||||
>>> customer_tax.save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
>>> output_loc, = Location.find([('code', '=', 'OUT')])
|
||||
|
||||
Create supplier consignment location::
|
||||
|
||||
>>> supplier_consignment_loc = Location()
|
||||
>>> supplier_consignment_loc.name = "Supplier Consignment"
|
||||
>>> supplier_consignment_loc.type = 'supplier'
|
||||
>>> supplier_consignment_loc.parent = storage_loc
|
||||
>>> supplier_consignment_loc.consignment_party = supplier
|
||||
>>> supplier_consignment_loc.save()
|
||||
|
||||
Create customer consignment location::
|
||||
|
||||
>>> customer_consignment_loc = Location()
|
||||
>>> customer_consignment_loc.name = "Customer Consignment"
|
||||
>>> customer_consignment_loc.type = 'storage'
|
||||
>>> customer_consignment_loc.parent = customer_loc
|
||||
>>> customer_consignment_loc.consignment_party = customer
|
||||
>>> customer_consignment_loc.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.supplier_taxes.append(supplier_tax)
|
||||
>>> account_category.customer_taxes.append(customer_tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_category = account_category
|
||||
>>> product_supplier = template.product_suppliers.new()
|
||||
>>> product_supplier.party = supplier
|
||||
>>> price = product_supplier.prices.new()
|
||||
>>> price.quantity = 1
|
||||
>>> price.unit_price = Decimal('5')
|
||||
>>> price = product_supplier.prices.new()
|
||||
>>> price.quantity = 2
|
||||
>>> price.unit_price = Decimal('4')
|
||||
>>> template.save()
|
||||
>>> product.template = template
|
||||
>>> product.save()
|
||||
|
||||
Fill supplier consignment location::
|
||||
|
||||
>>> Shipment = Model.get('stock.shipment.internal')
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = supplier_loc
|
||||
>>> shipment.to_location = supplier_consignment_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 10
|
||||
>>> move.from_location = supplier_loc
|
||||
>>> move.to_location = supplier_consignment_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
Use supplier consignment stock::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = supplier_consignment_loc
|
||||
>>> shipment.to_location = storage_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 4
|
||||
>>> move.from_location = supplier_consignment_loc
|
||||
>>> move.to_location = storage_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
Check supplier invoice line::
|
||||
|
||||
>>> InvoiceLine = Model.get('account.invoice.line')
|
||||
>>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'in')])
|
||||
>>> assertEqual(invoice_line.product, product)
|
||||
>>> invoice_line.quantity
|
||||
4.0
|
||||
>>> assertEqual(invoice_line.unit, unit)
|
||||
>>> invoice_line.unit_price
|
||||
Decimal('4.0000')
|
||||
>>> assertEqual(invoice_line.taxes, [supplier_tax])
|
||||
>>> move, = shipment.moves
|
||||
>>> assertEqual(move.origin, invoice_line)
|
||||
|
||||
Use supplier consignment stock for shipment out::
|
||||
|
||||
>>> ShipmentOut = Model.get('stock.shipment.out')
|
||||
>>> shipment_out = ShipmentOut()
|
||||
>>> shipment_out.customer = customer
|
||||
>>> shipment_out.warehouse = warehouse_loc
|
||||
>>> move = shipment_out.outgoing_moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 3
|
||||
>>> move.unit_price = Decimal('10')
|
||||
>>> move.currency = shipment_out.company.currency
|
||||
>>> move.from_location = output_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> shipment_out.click('wait')
|
||||
>>> move, = shipment_out.inventory_moves
|
||||
>>> move.from_location = supplier_consignment_loc
|
||||
>>> shipment_out.click('assign_try')
|
||||
>>> shipment_out.state
|
||||
'assigned'
|
||||
>>> move, = shipment_out.inventory_moves
|
||||
>>> len(move.invoice_lines)
|
||||
1
|
||||
|
||||
Fill customer consignment location::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = storage_loc
|
||||
>>> shipment.to_location = customer_consignment_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 3
|
||||
>>> move.from_location = storage_loc
|
||||
>>> move.to_location = customer_consignment_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
Use customer consignment stock::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = customer_consignment_loc
|
||||
>>> shipment.to_location = customer_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 1
|
||||
>>> move.from_location = customer_consignment_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
Check customer invoice line::
|
||||
|
||||
>>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'out')])
|
||||
>>> assertEqual(invoice_line.product, product)
|
||||
>>> invoice_line.quantity
|
||||
1.0
|
||||
>>> assertEqual(invoice_line.unit, unit)
|
||||
>>> invoice_line.unit_price
|
||||
Decimal('10.0000')
|
||||
>>> assertEqual(invoice_line.taxes, [customer_tax])
|
||||
>>> move, = shipment.moves
|
||||
>>> assertEqual(move.origin, invoice_line)
|
||||
|
||||
Duplicate shipment clear origin::
|
||||
|
||||
>>> duplicate, = shipment.duplicate()
|
||||
>>> move, = duplicate.moves
|
||||
>>> move.origin
|
||||
|
||||
Cancel supplier consignment stock::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = supplier_consignment_loc
|
||||
>>> shipment.to_location = storage_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 1
|
||||
>>> move.from_location = supplier_consignment_loc
|
||||
>>> move.to_location = storage_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> move, = shipment.moves
|
||||
>>> bool(move.origin)
|
||||
True
|
||||
>>> shipment.click('cancel')
|
||||
>>> shipment.state
|
||||
'cancelled'
|
||||
>>> move, = shipment.moves
|
||||
>>> bool(move.origin)
|
||||
False
|
||||
@@ -0,0 +1,159 @@
|
||||
===============================================
|
||||
Stock Consignment Supplier to Customer Scenario
|
||||
===============================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_consignment', create_company, create_chart)
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
>>> output_loc, = Location.find([('code', '=', 'OUT')])
|
||||
|
||||
Create customer consignment location::
|
||||
|
||||
>>> customer_consignment_loc = Location()
|
||||
>>> customer_consignment_loc.name = "Customer Consignment"
|
||||
>>> customer_consignment_loc.type = 'storage'
|
||||
>>> customer_consignment_loc.parent = customer_loc
|
||||
>>> customer_consignment_loc.consignment_party = customer
|
||||
>>> customer_consignment_loc.save()
|
||||
|
||||
Create supplier consignment location under customer consignment location::
|
||||
|
||||
>>> supplier_consignment_loc = Location()
|
||||
>>> supplier_consignment_loc.name = "Supplier Consignment"
|
||||
>>> supplier_consignment_loc.type = 'supplier'
|
||||
>>> supplier_consignment_loc.parent = customer_consignment_loc
|
||||
>>> supplier_consignment_loc.consignment_party = supplier
|
||||
>>> supplier_consignment_loc.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_category = account_category
|
||||
>>> product_supplier = template.product_suppliers.new()
|
||||
>>> product_supplier.party = supplier
|
||||
>>> price = product_supplier.prices.new()
|
||||
>>> price.quantity = 1
|
||||
>>> price.unit_price = Decimal('4')
|
||||
>>> template.save()
|
||||
>>> product.template = template
|
||||
>>> product.save()
|
||||
|
||||
Fill supplier consignment location::
|
||||
|
||||
>>> Shipment = Model.get('stock.shipment.internal')
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = supplier_loc
|
||||
>>> shipment.to_location = supplier_consignment_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 10
|
||||
>>> move.from_location = supplier_loc
|
||||
>>> move.to_location = supplier_consignment_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
Use supplier consignment stock by customer::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.from_location = supplier_consignment_loc
|
||||
>>> shipment.to_location = customer_loc
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 4
|
||||
>>> move.from_location = supplier_consignment_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
>>> move, = shipment.moves
|
||||
>>> len(move.invoice_lines)
|
||||
2
|
||||
>>> move.origin in move.invoice_lines
|
||||
True
|
||||
|
||||
Check supplier invoice line::
|
||||
|
||||
>>> InvoiceLine = Model.get('account.invoice.line')
|
||||
>>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'in')])
|
||||
>>> assertEqual(invoice_line.product, product)
|
||||
>>> invoice_line.quantity
|
||||
4.0
|
||||
>>> assertEqual(invoice_line.unit, unit)
|
||||
>>> invoice_line.unit_price
|
||||
Decimal('4.0000')
|
||||
>>> assertEqual(invoice_line.origin, move)
|
||||
|
||||
Check customer invoice line::
|
||||
|
||||
>>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'out')])
|
||||
>>> assertEqual(invoice_line.product, product)
|
||||
>>> invoice_line.quantity
|
||||
4.0
|
||||
>>> assertEqual(invoice_line.unit, unit)
|
||||
>>> invoice_line.unit_price
|
||||
Decimal('10.0000')
|
||||
>>> assertEqual(invoice_line.origin, move)
|
||||
13
modules/stock_consignment/tests/test_module.py
Normal file
13
modules/stock_consignment/tests/test_module.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# 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.tests.test_tryton import ModuleTestCase
|
||||
|
||||
|
||||
class StockConsignmentTestCase(ModuleTestCase):
|
||||
'Test Stock Consignment module'
|
||||
module = 'stock_consignment'
|
||||
extras = ['stock_supply']
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/stock_consignment/tests/test_scenario.py
Normal file
8
modules/stock_consignment/tests/test_scenario.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# 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.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
28
modules/stock_consignment/tryton.cfg
Normal file
28
modules/stock_consignment/tryton.cfg
Normal file
@@ -0,0 +1,28 @@
|
||||
[tryton]
|
||||
version=7.8.0
|
||||
depends:
|
||||
account_invoice
|
||||
account_invoice_line_standalone
|
||||
account_invoice_stock
|
||||
ir
|
||||
product
|
||||
purchase
|
||||
sale
|
||||
stock
|
||||
extras_depend:
|
||||
stock_supply
|
||||
xml:
|
||||
stock.xml
|
||||
|
||||
[register]
|
||||
model:
|
||||
stock.Location
|
||||
stock.LocationLeadTime
|
||||
stock.Move
|
||||
stock.ShipmentInternal
|
||||
stock.Inventory
|
||||
account.InvoiceLine
|
||||
|
||||
[register stock_supply]
|
||||
model:
|
||||
stock.OrderPoint
|
||||
10
modules/stock_consignment/view/location_form.xml
Normal file
10
modules/stock_consignment/view/location_form.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/form/field[@name='allow_pickup']" position="after">
|
||||
<label name="consignment_party"/>
|
||||
<field name="consignment_party"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</data>
|
||||
10
modules/stock_consignment/view/stock_move_form.xml
Normal file
10
modules/stock_consignment/view/stock_move_form.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page name="consignment_invoice_lines">
|
||||
<field name="consignment_invoice_lines" colspan="4"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
Reference in New Issue
Block a user