allow puchase_request on production operation #059903
This commit is contained in:
parent
8017f7c430
commit
566d47bd0c
11
__init__.py
11
__init__.py
|
@ -12,3 +12,14 @@ def register():
|
||||||
operation.OperationTracking,
|
operation.OperationTracking,
|
||||||
operation.Production,
|
operation.Production,
|
||||||
module='production_operation', type_='model')
|
module='production_operation', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
operation.OperationSubcontrat,
|
||||||
|
operation.PurchaseLine,
|
||||||
|
operation.PurchaseRequest,
|
||||||
|
depends=['purchase_request'],
|
||||||
|
module='production_operation', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
operation.CreatePurchase,
|
||||||
|
depends=['purchase_request'],
|
||||||
|
module='production_operation', type_='wizard')
|
||||||
|
|
||||||
|
|
12
message.xml
12
message.xml
|
@ -13,4 +13,16 @@ this repository contains the full copyright notices and license terms. -->
|
||||||
<field name="text">Production "%(production)s" can not be done because their operation "%(operation)s" is not done.'</field>
|
<field name="text">Production "%(production)s" can not be done because their operation "%(operation)s" is not done.'</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
|
<data grouped="1" depends="purchase_request">
|
||||||
|
<record model="ir.message" id="purchase_missing">
|
||||||
|
<field name="text">You can not finish an operation without create a purchase on Purchase request "%(request)s".</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="purchase_pending">
|
||||||
|
<field name="text">You can not finish an operation without finish purchase "%(purchase)s".</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="purchase_request_wait">
|
||||||
|
<field name="text">Operation "%(operation)s" on Production "%(production)s" already has a purchase request".</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|
215
operation.py
215
operation.py
|
@ -2,7 +2,7 @@ from decimal import Decimal
|
||||||
from trytond.model import (fields, ModelSQL, ModelView, Workflow,
|
from trytond.model import (fields, ModelSQL, ModelView, Workflow,
|
||||||
sequence_ordered)
|
sequence_ordered)
|
||||||
from trytond.pool import Pool, PoolMeta
|
from trytond.pool import Pool, PoolMeta
|
||||||
from trytond.pyson import Eval, If, Id
|
from trytond.pyson import Eval, If, Id, Bool, And
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
from trytond.i18n import gettext
|
from trytond.i18n import gettext
|
||||||
from trytond.exceptions import UserWarning, UserError
|
from trytond.exceptions import UserWarning, UserError
|
||||||
|
@ -128,7 +128,7 @@ class Operation(sequence_ordered(), Workflow, ModelSQL, ModelView):
|
||||||
|
|
||||||
invalid_productions = Production.search([
|
invalid_productions = Production.search([
|
||||||
('id', 'in', productions),
|
('id', 'in', productions),
|
||||||
('state', 'in', cls._invalid_production_states_on_create),
|
('state', 'in', ['done']),
|
||||||
], limit=1)
|
], limit=1)
|
||||||
|
|
||||||
if invalid_productions:
|
if invalid_productions:
|
||||||
|
@ -279,20 +279,30 @@ class Production(metaclass=PoolMeta):
|
||||||
'readonly': Eval('state') == 'done',
|
'readonly': Eval('state') == 'done',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def get_operation(self, route_operation):
|
||||||
|
Operation = Pool().get('production.operation')
|
||||||
|
values = Operation.default_get(
|
||||||
|
list(Operation._fields.keys()), with_rec_name=False)
|
||||||
|
|
||||||
|
operation = Operation(**values)
|
||||||
|
operation.sequence = route_operation.sequence
|
||||||
|
operation.work_center_category = route_operation.work_center_category
|
||||||
|
operation.work_center = route_operation.work_center
|
||||||
|
operation.operation_type = route_operation.operation_type
|
||||||
|
operation.route_operation = route_operation
|
||||||
|
if hasattr(Operation, 'subcontracted_product'):
|
||||||
|
operation.subcontracted_product = (
|
||||||
|
route_operation.subcontracted_product)
|
||||||
|
return operation
|
||||||
|
|
||||||
@fields.depends('route', 'operations')
|
@fields.depends('route', 'operations')
|
||||||
def on_change_route(self):
|
def on_change_route(self):
|
||||||
Operation = Pool().get('production.operation')
|
Operation = Pool().get('production.operation')
|
||||||
self.operations = None
|
self.operations = None
|
||||||
operations = []
|
operations = []
|
||||||
if self.route:
|
if self.route:
|
||||||
for operation in self.route.operations:
|
for route_operation in self.route.operations:
|
||||||
operation = Operation(
|
operation = self.get_operation(route_operation)
|
||||||
sequence=operation.sequence,
|
|
||||||
work_center_category=operation.work_center_category,
|
|
||||||
work_center=operation.work_center,
|
|
||||||
operation_type=operation.operation_type,
|
|
||||||
route_operation=operation,
|
|
||||||
)
|
|
||||||
operations.append(operation)
|
operations.append(operation)
|
||||||
self.operations = operations
|
self.operations = operations
|
||||||
|
|
||||||
|
@ -395,3 +405,188 @@ class Production(metaclass=PoolMeta):
|
||||||
operation_type=route_operation.operation_type,
|
operation_type=route_operation.operation_type,
|
||||||
route_operation=route_operation,
|
route_operation=route_operation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OperationSubcontrat(metaclass=PoolMeta):
|
||||||
|
__name__ = 'production.operation'
|
||||||
|
|
||||||
|
subcontracted_product = fields.Many2One('product.product',
|
||||||
|
'Subcontracted product', domain=[('type', '=', 'service')])
|
||||||
|
purchase_request = fields.Many2One('purchase.request', 'Purchase Request')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __setup__(cls):
|
||||||
|
super().__setup__()
|
||||||
|
cls._buttons.update({
|
||||||
|
'create_purchase_request': {
|
||||||
|
'invisible': ((Eval('state') != 'planned') |
|
||||||
|
~Bool(Eval('subcontracted_product',-1))),
|
||||||
|
'readonly': (Bool(Eval('purchase_request',-1)))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
def _get_purchase_request(self):
|
||||||
|
pool = Pool()
|
||||||
|
Request = pool.get('purchase.request')
|
||||||
|
|
||||||
|
product = self.subcontracted_product
|
||||||
|
uom = product.purchase_uom
|
||||||
|
quantity = 1
|
||||||
|
shortage_date = self.production.planned_date
|
||||||
|
company = self.production.company
|
||||||
|
supplier_pattern = {}
|
||||||
|
supplier_pattern['company'] = company.id
|
||||||
|
supplier, purchase_date = Request.find_best_supplier(product,
|
||||||
|
shortage_date, **supplier_pattern)
|
||||||
|
|
||||||
|
|
||||||
|
location = self.production.warehouse
|
||||||
|
request = Request(product=product,
|
||||||
|
party=None,
|
||||||
|
quantity=quantity,
|
||||||
|
uom=uom,
|
||||||
|
purchase_date=purchase_date,
|
||||||
|
supply_date=shortage_date,
|
||||||
|
company=company,
|
||||||
|
warehouse=location.id,
|
||||||
|
origin=self,
|
||||||
|
)
|
||||||
|
return request
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@ModelView.button
|
||||||
|
def create_purchase_request(cls, operations):
|
||||||
|
pool = Pool()
|
||||||
|
to_save = []
|
||||||
|
for operation in operations:
|
||||||
|
if not operation.subcontracted_product:
|
||||||
|
continue
|
||||||
|
request = operation._get_purchase_request()
|
||||||
|
operation.purchase_request = request
|
||||||
|
to_save.append(operation)
|
||||||
|
cls.save(to_save)
|
||||||
|
|
||||||
|
def get_cost(self, name):
|
||||||
|
pool = Pool()
|
||||||
|
cost = super().get_cost(name)
|
||||||
|
if self.purchase_request and self.purchase_request.purchase_line:
|
||||||
|
cost += self.purchase_request.purchase_line.amount
|
||||||
|
return cost
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wait(cls, operations):
|
||||||
|
pool = Pool()
|
||||||
|
Config = pool.get('production.configuration')
|
||||||
|
Warning = pool.get('res.user.warning')
|
||||||
|
op_warn = []
|
||||||
|
config = Config(1)
|
||||||
|
|
||||||
|
if config.check_state_operation == 'user_warning':
|
||||||
|
op_warn = [op for op in operations if op.purchase_request]
|
||||||
|
if op_warn:
|
||||||
|
operation, = op_warn
|
||||||
|
key ='operation_%d' % operation.id
|
||||||
|
if Warning.check(key):
|
||||||
|
raise UserWarning(key,
|
||||||
|
gettext('production_operation.purchase_request_wait',
|
||||||
|
production=operation.production.rec_name,
|
||||||
|
operation=operation.rec_name))
|
||||||
|
|
||||||
|
super().wait(operations)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def done(cls, operations):
|
||||||
|
pool = Pool()
|
||||||
|
Purchase = pool.get('purchase.purchase')
|
||||||
|
requests = set([o.purchase_request for o in operations if
|
||||||
|
o.purchase_request])
|
||||||
|
purchases = [r.purchase for r in requests if r.purchase]
|
||||||
|
|
||||||
|
for request in requests:
|
||||||
|
if request.purchase:
|
||||||
|
continue
|
||||||
|
raise UserError(
|
||||||
|
gettext('production_operation.purchase_missing',
|
||||||
|
request=request.rec_name))
|
||||||
|
|
||||||
|
for purchase in purchases:
|
||||||
|
if purchase.state in ('processing', 'done'):
|
||||||
|
continue
|
||||||
|
raise UserError(
|
||||||
|
gettext('production_operation.purchase_pending',
|
||||||
|
purchase=purchase.rec_name))
|
||||||
|
|
||||||
|
super().done(operations)
|
||||||
|
Purchase.process(purchases)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def copy(cls, operations, default=None):
|
||||||
|
if default is None:
|
||||||
|
default = {}
|
||||||
|
else:
|
||||||
|
default = default.copy()
|
||||||
|
default.setdefault('purchase_request', None)
|
||||||
|
super().copy(operations, default=default)
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseLine(metaclass=PoolMeta):
|
||||||
|
__name__ = 'purchase.line'
|
||||||
|
|
||||||
|
origin = fields.Reference('Origin', selection='get_origin', select=True,
|
||||||
|
states={
|
||||||
|
'readonly': Eval('state') != 'draft',
|
||||||
|
},
|
||||||
|
depends=['state'])
|
||||||
|
|
||||||
|
def _get_invoice_line_quantity(self):
|
||||||
|
pool = Pool()
|
||||||
|
ProductionOperation = pool.get('production.operation')
|
||||||
|
if not isinstance(self.origin, ProductionOperation):
|
||||||
|
return super()._get_invoice_line_quantity()
|
||||||
|
|
||||||
|
if not (self.purchase.invoice_method == 'shipment'
|
||||||
|
and self.origin.state == 'done'):
|
||||||
|
return 0
|
||||||
|
return super()._get_invoice_line_quantity()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_origin(cls):
|
||||||
|
'Return list of Model names for origin Reference'
|
||||||
|
return [cls.__name__, 'production.operation']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_origin(cls):
|
||||||
|
IrModel = Pool().get('ir.model')
|
||||||
|
get_name = IrModel.get_name
|
||||||
|
models = cls._get_origin()
|
||||||
|
return [(None, '')] + [(m, get_name(m)) for m in models]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def copy(cls, lines, default=None):
|
||||||
|
if default is None:
|
||||||
|
default = {}
|
||||||
|
else:
|
||||||
|
default = default.copy()
|
||||||
|
default.setdefault('origin', None)
|
||||||
|
super().copy(lines, default=default)
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseRequest(metaclass=PoolMeta):
|
||||||
|
__name__ = 'purchase.request'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_origin(cls):
|
||||||
|
return super()._get_origin() | {'production.operation'}
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePurchase(metaclass=PoolMeta):
|
||||||
|
__name__ = 'purchase.request.create_purchase'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compute_purchase_line(cls, key, requests, purchase):
|
||||||
|
|
||||||
|
line = super().compute_purchase_line(key, requests, purchase)
|
||||||
|
if requests:
|
||||||
|
request = requests[0]
|
||||||
|
line.origin = request.origin
|
||||||
|
return line
|
||||||
|
|
|
@ -211,4 +211,24 @@
|
||||||
sequence="10"
|
sequence="10"
|
||||||
name="Operations"/>
|
name="Operations"/>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
|
<data depends="purchase_request">
|
||||||
|
<record model="ir.ui.view" id="subcontract_operation_view_form">
|
||||||
|
<field name="model">production.operation</field>
|
||||||
|
<field name="inherit" ref="production_operation.production_operation_view_form"/>
|
||||||
|
<field name="name">subcontracted_operation_form</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="subcontract_operation_view_list">
|
||||||
|
<field name="model">production.operation</field>
|
||||||
|
<field name="inherit" ref="production_operation.production_operation_view_list"/>
|
||||||
|
<field name="name">subcontracted_operation_list</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.model.button" id="create_purchase_request_button">
|
||||||
|
<field name="name">create_purchase_request</field>
|
||||||
|
<field name="string">Create Purchase Request</field>
|
||||||
|
<field name="model" search="[('model', '=', 'production.operation')]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<tryton>
|
||||||
|
<data depends='purchase_request'>
|
||||||
|
<record model="ir.ui.view" id="purchase_line_view_form">
|
||||||
|
<field name="model">purchase.line</field>
|
||||||
|
<field name="inherit" ref="purchase.purchase_line_view_form"/>
|
||||||
|
<field name="name">purchase_line_form</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="purchase_line_view_tree_sequence">
|
||||||
|
<field name="model">purchase.line</field>
|
||||||
|
<field name="inherit"
|
||||||
|
ref="purchase.purchase_line_view_tree_sequence"/>
|
||||||
|
<field name="name">purchase_line_tree</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</tryton>
|
|
@ -15,7 +15,7 @@ Imports::
|
||||||
|
|
||||||
Install production_operation Module::
|
Install production_operation Module::
|
||||||
|
|
||||||
>>> config = activate_modules('production_operation')
|
>>> config = activate_modules(['production_operation', 'purchase_request'])
|
||||||
|
|
||||||
Create company::
|
Create company::
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,9 @@ depends:
|
||||||
production_route
|
production_route
|
||||||
extras_depend:
|
extras_depend:
|
||||||
stock_supply_production
|
stock_supply_production
|
||||||
|
purchase_request
|
||||||
xml:
|
xml:
|
||||||
configuration.xml
|
configuration.xml
|
||||||
operation.xml
|
operation.xml
|
||||||
message.xml
|
message.xml
|
||||||
|
purchase.xml
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="/form/notebook/page/field[@name='product_supplier']"
|
||||||
|
position="after">
|
||||||
|
<newline/>
|
||||||
|
<label name="origin"/>
|
||||||
|
<field name="origin" colspan="3"/>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="/tree/field[@name='product_supplier']" position="after">
|
||||||
|
<field name="origin"/>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="/form/field[@name='sequence']" position="after">
|
||||||
|
<newline/>
|
||||||
|
<label name="subcontracted_product"/>
|
||||||
|
<field name="subcontracted_product"/>
|
||||||
|
<label name="purchase_request"/>
|
||||||
|
<field name="purchase_request"/>
|
||||||
|
<button name="create_purchase_request" colspan="2"/>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="/tree/field[@name='state']" position="before">
|
||||||
|
<field name="subcontracted_product"/>
|
||||||
|
<field name="purchase_request"/>
|
||||||
|
<button name="create_purchase_request"/>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
Loading…
Reference in New Issue