Initial import from Docker volume

This commit is contained in:
root
2025-12-26 13:11:43 +00:00
commit 4998dc066a
13336 changed files with 1767801 additions and 0 deletions

View 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 product, production, sale
__all__ = ['register']
def register():
Pool.register(
product.Template,
product.Product,
production.Production,
sale.Sale,
sale.Line,
module='sale_supply_production', type_='model')
Pool.register(
module='sale_supply_production', type_='wizard')
Pool.register(
module='sale_supply_production', type_='report')

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Produccions"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Produccions"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"No podeu restablir la producció \"%(production)s\" a esborrany perquè s'ha "
"generat a partir d'una venda."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Produktionsaufträge"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Produktionsaufträge"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"Produktionsauftrag \"%(production)s\" kann nicht auf Entwurf zurück gesetzt "
"werden, weil er durch einen Verkauf generiert wurde."

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Producciones"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Producciones"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"No puede restablecer la producción \"%(production)s\" a borrador ya que ha "
"sido generada por una venta."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Productions"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Productions"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"Vous ne pouvez pas réinitialiser à l'état brouillon la production "
"« %(production)s » car elle a été générée par une vente."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Produzioni"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Produzioni"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"Non puoi reimpostare la produzione \"%(production)s\" come bozza perché tale"
" è stata generata da una vendita."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Producties"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Producties"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"U kunt de productie \"%(production)s\" niet terugzetten naar concept omdat "
"deze is gegenereerd door een verkoop."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,19 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr "Producții"
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr "Producții"
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""
"\"%(production)s\" nu poate fi resetat la draft pentru că a fost generat de "
"o vânzare."

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View File

@@ -0,0 +1,17 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.line,productions:"
msgid "Productions"
msgstr ""
msgctxt "model:ir.action,name:act_production_form"
msgid "Productions"
msgstr ""
msgctxt "model:ir.message,text:msg_production_reset_draft"
msgid ""
"You cannot reset production \"%(production)s\" to draft because it was "
"generated by a sale."
msgstr ""

View 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. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_production_reset_draft">
<field name="text">You cannot reset production "%(production)s" to draft because it was generated by a sale.</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,18 @@
# 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 PoolMeta
from trytond.pyson import Eval
class Template(metaclass=PoolMeta):
__name__ = 'product.template'
@classmethod
def __setup__(cls):
super().__setup__()
cls.supply_on_sale.states['invisible'] &= (
~Eval('producible') | ~Eval('salable'))
class Product(metaclass=PoolMeta):
__name__ = 'product.product'

View 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. -->
<tryton>
<data>
<record model="ir.ui.view" id="template_view_form">
<field name="model">product.template</field>
<field name="inherit" ref="product.template_view_form"/>
<field name="name">template_form</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,119 @@
# 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 collections import defaultdict
from functools import wraps
from trytond.i18n import gettext
from trytond.model import Model, ModelView, Workflow
from trytond.model.exceptions import AccessError
from trytond.pool import Pool, PoolMeta
from trytond.tools import grouped_slice
from trytond.transaction import Transaction, without_check_access
def process_sale_supply(func):
@wraps(func)
def wrapper(cls, productions):
pool = Pool()
Sale = pool.get('sale.sale')
transaction = Transaction()
context = transaction.context
sales = set()
with without_check_access():
for sub_productions in grouped_slice(productions):
ids = [p.id for p in sub_productions]
sales.update([s.id for s in Sale.search([
('lines.productions', 'in', ids),
])])
func(cls, productions)
if sales:
with transaction.set_context(
queue_batch=context.get('queue_batch', True)):
Sale.__queue__.process(sales)
return wrapper
class Production(metaclass=PoolMeta):
__name__ = 'production'
@classmethod
def _get_origin(cls):
return super()._get_origin() | {'sale.line'}
@classmethod
@process_sale_supply
def delete(cls, productions):
super().delete(productions)
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
@process_sale_supply
def cancel(cls, productions):
super().cancel(productions)
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, productions):
pool = Pool()
SaleLine = pool.get('sale.line')
for production in productions:
if (production.state == 'cancelled'
and isinstance(production.origin, SaleLine)):
raise AccessError(
gettext('sale_supply_production'
'.msg_production_reset_draft',
production=production.rec_name))
super().draft(productions)
@classmethod
@ModelView.button
@Workflow.transition('running')
@process_sale_supply
def run(cls, productions):
super().run(productions)
@classmethod
@ModelView.button
@Workflow.transition('done')
@process_sale_supply
def do(cls, productions):
super().do(productions)
for production in productions:
production.assign_supplied()
def assign_supplied(self, grouping=('product',), filter_=None):
pool = Pool()
SaleLine = pool.get('sale.line')
if isinstance(self.origin, SaleLine):
sale_line = self.origin
else:
return
def filter_func(move):
if filter_ is None:
return True
for fieldname, values in filter_:
value = getattr(move, fieldname)
if isinstance(value, Model):
value = value.id
if value not in values:
return False
def get_key(move):
key = (move.to_location.id,)
for field in grouping:
value = getattr(move, field)
if isinstance(value, Model):
value = value.id
key += (value,)
return key
pbl = defaultdict(lambda: defaultdict(int))
for move in filter(filter_func, self.outputs):
pbl[move.product][get_key(move)] += move.internal_quantity
sale_line.assign_supplied(pbl[sale_line.product], grouping=grouping)

View File

@@ -0,0 +1,113 @@
# 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 datetime as dt
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
@classmethod
def _process_supply(cls, sales, product_quantities):
pool = Pool()
Production = pool.get('production')
productions = []
for sale in sales:
productions.extend(sale.create_productions(product_quantities))
Production.save(productions)
Production.set_moves(productions)
super()._process_supply(sales, product_quantities)
def create_productions(self, product_quantities):
productions = []
for line in self.lines:
production = line.get_production(product_quantities)
if not production:
continue
production.set_planned_start_date()
productions.append(production)
assert not line.productions
return productions
class Line(metaclass=PoolMeta):
__name__ = 'sale.line'
productions = fields.One2Many(
'production', 'origin', "Productions", readonly=True)
@property
def has_supply(self):
return super().has_supply or bool(self.productions)
def get_supply_state(self, name):
state = super().get_supply_state(name)
if self.productions:
states = {p.state for p in self.productions}
if states <= {'running', 'done', 'cancelled'}:
if states == {'cancelled'}:
state = 'cancelled'
else:
state = 'supplied'
else:
state = 'requested'
return state
@classmethod
def copy(cls, lines, default=None):
if default is None:
default = {}
else:
default = default.copy()
default.setdefault('productions', None)
return super().copy(lines, default=default)
def get_production(self, product_quantities):
"Return production for the sale line"
pool = Pool()
Production = pool.get('production')
Date = pool.get('ir.date')
Uom = pool.get('product.uom')
with Transaction().set_context(company=self.sale.company.id):
today = Date.today()
if (not self.supply_on_sale
or self.productions
or not self.ready_for_supply
or not self.product.producible):
return
product = self.product
quantity = self._get_move_quantity('out')
if product.supply_on_sale == 'stock_first':
available_qty = product_quantities[product]
available_qty = Uom.compute_qty(
product.default_uom, available_qty, self.unit,
round=False)
if quantity < available_qty:
product_quantities[product] -= Uom.compute_qty(
self.unit, quantity, product.default_uom, round=False)
return
date = self.shipping_date or today
if date <= today:
date = today
else:
date -= dt.timedelta(1)
return Production(
planned_date=date,
company=self.sale.company,
warehouse=self.warehouse,
location=self.warehouse.production_location,
product=product,
bom=product.boms[0].bom if product.boms else None,
unit=self.unit,
quantity=quantity,
state='request',
origin=self,
)

View File

@@ -0,0 +1,20 @@
<?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.action.act_window" id="act_production_form">
<field name="name">Productions</field>
<field name="res_model">production</field>
<field
name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('origin.sale.id', '=', Eval('active_id'), 'sale.line'), ('origin.sale.id', 'in', Eval('active_ids'), 'sale.line'))]"
pyson="1"/>
</record>
<record model="ir.action.keyword" id="act_production_form_keyword1">
<field name="keyword">form_relate</field>
<field name="model">sale.sale,-1</field>
<field name="action" ref="act_production_form"/>
</record>
</data>
</tryton>

View 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.

View File

@@ -0,0 +1,157 @@
===============================
Sale Supply Production 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, get_company
>>> from trytond.tests.tools import activate_modules, assertEqual
Activate modules::
>>> config = activate_modules('sale_supply_production')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
Create parties::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
Create account category::
>>> ProductCategory = Model.get('product.category')
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_revenue = accounts['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')
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.producible = True
>>> template.salable = True
>>> template.supply_on_sale = 'always'
>>> template.list_price = Decimal(30)
>>> template.account_category = account_category
>>> product, = template.products
>>> product.cost_price = Decimal(20)
>>> template.save()
>>> product, = template.products
Create components::
>>> template = ProductTemplate()
>>> template.name = "Component 1"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(5)
>>> component, = template.products
>>> component.cost_price = Decimal(1)
>>> template.save()
>>> component, = template.products
Create bill of material::
>>> BOM = Model.get('production.bom')
>>> bom = BOM(name="Product")
>>> input = bom.inputs.new()
>>> input.product = component
>>> input.quantity = 5
>>> output = bom.outputs.new()
>>> output.product = product
>>> output.quantity = 1
>>> bom.save()
>>> product_bom = product.boms.new()
>>> product_bom.bom = bom
>>> product.save()
Sale 10 products::
>>> Sale = Model.get('sale.sale')
>>> sale = Sale()
>>> sale.party = customer
>>> line = sale.lines.new()
>>> line.product = product
>>> line.quantity = 10
>>> sale.click('quote')
>>> sale.click('confirm')
>>> sale.state
'processing'
>>> shipment, = sale.shipments
>>> move, = shipment.outgoing_moves
>>> move.state
'staging'
>>> move, = shipment.inventory_moves
>>> move.state
'staging'
Check the production::
>>> Production = Model.get('production')
>>> production, = Production.find([])
>>> production.state
'request'
>>> assertEqual(production.origin, sale.lines[0])
>>> assertEqual(production.product, product)
>>> assertEqual(production.bom, bom)
>>> production.quantity
10.0
Delete the production, recreate one::
>>> production.delete()
>>> production, = Production.find([])
>>> production.quantity
10.0
Start the production::
>>> production.click('draft')
>>> production.click('wait')
>>> production.click('assign_force')
>>> production.click('run')
>>> production.state
'running'
>>> shipment.reload()
>>> move, = shipment.outgoing_moves
>>> move.state
'draft'
>>> move, = shipment.inventory_moves
>>> move.state
'draft'
Finish the production::
>>> production.click('do')
>>> shipment.reload()
>>> move, = shipment.outgoing_moves
>>> move.state
'draft'
>>> move, = shipment.inventory_moves
>>> move.state
'assigned'

View File

@@ -0,0 +1,110 @@
===========================================
Sale Supply Production Stock First 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
Activate modules::
>>> config = activate_modules('sale_supply_production')
>>> Inventory = Model.get('stock.inventory')
>>> Location = Model.get('stock.location')
>>> Party = Model.get('party.party')
>>> ProductCategory = Model.get('product.category')
>>> ProductTemplate = Model.get('product.template')
>>> Sale = Model.get('sale.sale')
>>> UoM = Model.get('product.uom')
Create company::
>>> _ = create_company()
Create chart of accounts::
>>> _ = create_chart()
>>> accounts = get_accounts()
Create parties::
>>> customer = Party(name='Customer')
>>> customer.save()
Create account category::
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_revenue = accounts['revenue']
>>> account_category.save()
Create product::
>>> unit, = UoM.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.producible = True
>>> template.salable = True
>>> template.supply_on_sale = 'stock_first'
>>> template.list_price = Decimal(30)
>>> template.account_category = account_category
>>> product, = template.products
>>> product.cost_price = Decimal(20)
>>> template.save()
>>> product, = template.products
Fill warehouse::
>>> inventory = Inventory()
>>> inventory.location, = Location.find([('code', '=', 'STO')])
>>> line = inventory.lines.new()
>>> line.product = product
>>> line.quantity = 5
>>> inventory.click('confirm')
>>> inventory.state
'done'
Sale 3 products without production request::
>>> sale = Sale()
>>> sale.party = customer
>>> line = sale.lines.new()
>>> line.product = product
>>> line.quantity = 3
>>> sale.click('quote')
>>> sale.click('confirm')
>>> sale.state
'processing'
>>> line, = sale.lines
>>> len(line.productions)
0
>>> move, = line.moves
>>> move.state
'draft'
Sale 4 products with production request::
>>> sale = Sale()
>>> sale.party = customer
>>> line = sale.lines.new()
>>> line.product = product
>>> line.quantity = 4
>>> sale.click('quote')
>>> sale.click('confirm')
>>> sale.state
'processing'
>>> line, = sale.lines
>>> len(line.productions)
1
>>> move, = line.moves
>>> move.state
'staging'

View File

@@ -0,0 +1,12 @@
# 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 SaleSupplyProductionTestCase(ModuleTestCase):
'Test Sale Supply Production module'
module = 'sale_supply_production'
del ModuleTestCase

View 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)

View File

@@ -0,0 +1,10 @@
[tryton]
version=7.2.0
depends:
ir
production
sale_supply
xml:
sale.xml
product.xml
message.xml

View 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="//page[@id='production']/field[@name='producible']" position="after">
<label name="supply_on_sale"/>
<field name="supply_on_sale"/>
<newline/>
</xpath>
</data>