Handle packing exceptions
This commit is contained in:
1 parent
6b15d23e24
commit
472365d048
2 files changed
+222
-29
No files matched your search
@@ -5,6 +5,7 @@
|
||||
from trytond.osv import fields, OSV
|
||||
from trytond.netsvc import LocalService
|
||||
from trytond.report import CompanyReport
|
||||
from trytond.wizard import Wizard, WizardOSV
|
||||
from trytond.backend import TableHandler
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -837,13 +838,6 @@ class Sale(OSV):
|
||||
'waiting', cursor, context=ctx)
|
||||
return packing_id
|
||||
|
||||
def ignore_packing_exception(self, cursor, user, sale_id, context=None):
|
||||
line_obj = self.pool.get('sale.line')
|
||||
|
||||
sale = self.browse(cursor, user, sale_id, context=context)
|
||||
for line in sale.lines:
|
||||
line_obj.ignore_move_exception(cursor, user, line, context=context)
|
||||
|
||||
Sale()
|
||||
|
||||
|
||||
@@ -918,6 +912,9 @@ class SaleLine(OSV):
|
||||
readonly=True, select=1)
|
||||
moves_ignored = fields.Many2Many('stock.move', 'sale_line_moves_ignored_rel',
|
||||
'sale_line', 'move', 'Moves Ignored', readonly=True)
|
||||
moves_duplicated = fields.Many2Many('stock.move',
|
||||
'sale_line_moves_duplicated_rel', 'sale_line', 'move',
|
||||
'Moves Duplicated', readonly=True)
|
||||
move_done = fields.Function('get_move_done', type='boolean',
|
||||
string='Moves Done')
|
||||
move_exception = fields.Function('get_move_exception', type='boolean',
|
||||
@@ -980,11 +977,12 @@ class SaleLine(OSV):
|
||||
if line.product.type == 'service':
|
||||
res[line.id] = True
|
||||
continue
|
||||
ignored_ids = [x.id for x in line.moves_ignored]
|
||||
skip_ids = set(x.id for x in line.moves_ignored)
|
||||
skip_ids.update(x.id for x in line.moves_duplicated)
|
||||
quantity = line.quantity
|
||||
for move in line.moves:
|
||||
if move.state != 'done' \
|
||||
and move.id not in ignored_ids:
|
||||
and move.id not in skip_ids:
|
||||
val = False
|
||||
break
|
||||
quantity -= uom_obj.compute_qty(cursor, user, move.uom,
|
||||
@@ -999,10 +997,11 @@ class SaleLine(OSV):
|
||||
res = {}
|
||||
for line in self.browse(cursor, user, ids, context=context):
|
||||
val = False
|
||||
ignored_ids = [x.id for x in line.moves_ignored]
|
||||
skip_ids = set(x.id for x in line.moves_ignored)
|
||||
skip_ids.update(x.id for x in line.moves_duplicated)
|
||||
for move in line.moves:
|
||||
if move.state == 'cancel' \
|
||||
and move.id not in ignored_ids:
|
||||
and move.id not in skip_ids:
|
||||
val = True
|
||||
break
|
||||
res[line.id] = val
|
||||
@@ -1187,6 +1186,7 @@ class SaleLine(OSV):
|
||||
default = default.copy()
|
||||
default['moves'] = False
|
||||
default['moves_ignored'] = False
|
||||
default['moves_duplicated'] = False
|
||||
default['invoice_lines'] = False
|
||||
return super(SaleLine, self).copy(cursor, user, ids,
|
||||
default=default, context=context)
|
||||
@@ -1211,10 +1211,12 @@ class SaleLine(OSV):
|
||||
return
|
||||
if line.product.type == 'service':
|
||||
return
|
||||
skip_ids = set(x.id for x in line.moves_duplicated)
|
||||
quantity = line.quantity
|
||||
for move in line.moves:
|
||||
quantity -= uom_obj.compute_qty(cursor, user, move.uom,
|
||||
move.quantity, line.unit, context=context)
|
||||
if move.id not in skip_ids:
|
||||
quantity -= uom_obj.compute_qty(cursor, user, move.uom,
|
||||
move.quantity, line.unit, context=context)
|
||||
if quantity <= 0.0:
|
||||
return
|
||||
if not line.sale.party.customer_location:
|
||||
@@ -1232,16 +1234,6 @@ class SaleLine(OSV):
|
||||
res['planned_date'] = line.sale.sale_date
|
||||
return res
|
||||
|
||||
def ignore_move_exception(self, cursor, user, line, context=None):
|
||||
move_ids = []
|
||||
for move in line.moves:
|
||||
if move.state == 'cancel':
|
||||
move_ids.append(move.id)
|
||||
if move_ids:
|
||||
self.write(cursor, user, line.id, {
|
||||
'moves_ignored': [('add', x) for x in move_ids],
|
||||
}, context=context)
|
||||
|
||||
SaleLine()
|
||||
|
||||
|
||||
@@ -1357,6 +1349,13 @@ Product()
|
||||
class PackingOut(OSV):
|
||||
_name = 'stock.packing.out'
|
||||
|
||||
def __init__(self):
|
||||
super(PackingOut, self).__init__()
|
||||
self._error_messages.update({
|
||||
'reset_move': 'You cannot reset to draft a move generated '\
|
||||
'by a sale.',
|
||||
})
|
||||
|
||||
def write(self, cursor, user, ids, vals, context=None):
|
||||
workflow_service = LocalService('workflow')
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
@@ -1386,6 +1385,15 @@ class PackingOut(OSV):
|
||||
'packing_update', cursor, context=context)
|
||||
return res
|
||||
|
||||
def button_draft(self, cursor, user, ids, context=None):
|
||||
for packing in self.browse(cursor, user, ids, context=context):
|
||||
for move in packing.outgoing_moves:
|
||||
if move.state == 'cancel' and move.sale_line:
|
||||
self.raise_user_error(cursor, 'reset_move')
|
||||
|
||||
return super(PackingIn, self).button_draft(
|
||||
cursor, user, ids, context=context)
|
||||
|
||||
PackingOut()
|
||||
|
||||
|
||||
@@ -1399,6 +1407,12 @@ class Move(OSV):
|
||||
sale = fields.Function('get_sale', type='many2one',
|
||||
relation='sale.sale', string='Sale',
|
||||
fnct_search='search_sale', select=1)
|
||||
sale_exception_state = fields.Function('get_sale_exception_state',
|
||||
type='selection',
|
||||
selection=[('', ''),
|
||||
('ignored', 'Ignored'),
|
||||
('duplicated', 'Duplicated')],
|
||||
string='Exception State')
|
||||
|
||||
def get_sale(self, cursor, user, ids, name, arg, context=None):
|
||||
sale_obj = self.pool.get('sale.sale')
|
||||
@@ -1430,6 +1444,18 @@ class Move(OSV):
|
||||
i += 1
|
||||
return args2
|
||||
|
||||
def get_sale_exception_state(self, cursor, user, ids, name, arg,
|
||||
context=None):
|
||||
res = {}.fromkeys(ids, '')
|
||||
for move in self.browse(cursor, user, ids, context=context):
|
||||
if not move.sale_line:
|
||||
continue
|
||||
if move.id in (x.id for x in move.sale_line.moves_duplicated):
|
||||
res[move.id] = 'duplicated'
|
||||
if move.id in (x.id for x in move.sale_line.moves_ignored):
|
||||
res[move.id] = 'ignored'
|
||||
return res
|
||||
|
||||
def write(self, cursor, user, ids, vals, context=None):
|
||||
workflow_service = LocalService('workflow')
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
@@ -1479,3 +1505,127 @@ class Invoice(OSV):
|
||||
context=context)
|
||||
|
||||
Invoice()
|
||||
|
||||
|
||||
class HandlePackingExceptionAsk(WizardOSV):
|
||||
'Packing Exception Ask'
|
||||
_name = 'sale.handle.packing.exception.ask'
|
||||
_description = __doc__
|
||||
|
||||
duplicate_moves = fields.Many2Many(
|
||||
'stock.move', None, None, None, 'Duplicate Moves',
|
||||
domain="[('id', 'in', domain_moves)]", depends=['domain_moves'])
|
||||
domain_moves = fields.Many2Many(
|
||||
'stock.move', None, None, None, 'Domain Moves')
|
||||
|
||||
def default_duplicate_moves(self, cursor, user, context=None):
|
||||
return self.default_domain_moves(cursor, user, context=context)
|
||||
|
||||
def default_domain_moves(self, cursor, user, context=None):
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
active_id = context and context.get('active_id')
|
||||
if not active_id:
|
||||
return []
|
||||
|
||||
line_ids = sale_line_obj.search(
|
||||
cursor, user, [('sale', '=', active_id)],
|
||||
context=context)
|
||||
lines = sale_line_obj.browse(cursor, user, line_ids, context=context)
|
||||
|
||||
domain_moves = []
|
||||
for line in lines:
|
||||
skip_ids = set(x.id for x in line.moves_ignored)
|
||||
skip_ids.update(x.id for x in line.moves_duplicated)
|
||||
for move in line.moves:
|
||||
if move.state == 'cancel' and move.id not in skip_ids:
|
||||
domain_moves.append(move.id)
|
||||
|
||||
return domain_moves
|
||||
|
||||
HandlePackingExceptionAsk()
|
||||
|
||||
class HandlePackingException(Wizard):
|
||||
'Handle Packing Exception'
|
||||
_name = 'sale.handle.packing.exception'
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [],
|
||||
'result': {
|
||||
'type': 'form',
|
||||
'object': 'sale.handle.packing.exception.ask',
|
||||
'state': [
|
||||
('end', 'Cancel', 'tryton-cancel'),
|
||||
('ok', 'Ok', 'tryton-ok', True),
|
||||
],
|
||||
},
|
||||
},
|
||||
'ok': {
|
||||
'result': {
|
||||
'type': 'action',
|
||||
'action': '_handle_moves',
|
||||
'state': 'end',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def _handle_moves(self, cursor, user, data, context=None):
|
||||
workflow_service = LocalService('workflow')
|
||||
sale_obj = self.pool.get('sale.sale')
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
move_obj = self.pool.get('stock.move')
|
||||
packing_obj = self.pool.get('stock.packing.out')
|
||||
to_duplicate = data['form']['duplicate_moves'][0][1]
|
||||
domain_moves = data['form']['domain_moves'][0][1]
|
||||
|
||||
sale = sale_obj.browse(cursor, user, data['id'], context=context)
|
||||
|
||||
duplicate_all = []
|
||||
for line in sale.lines:
|
||||
moves_ignored = []
|
||||
moves_duplicated = []
|
||||
skip_ids = set(x.id for x in line.moves_ignored)
|
||||
skip_ids.update(x.id for x in line.moves_duplicated)
|
||||
for move in line.moves:
|
||||
if move.id not in domain_moves or move.id in skip_ids:
|
||||
continue
|
||||
if move.id in to_duplicate:
|
||||
moves_duplicated.append(move.id)
|
||||
else:
|
||||
moves_ignored.append(move.id)
|
||||
|
||||
duplicate_all.extend(moves_duplicated)
|
||||
sale_line_obj.write(
|
||||
cursor, user, line.id,
|
||||
{'moves_ignored': [('add', moves_ignored)],
|
||||
'moves_duplicated': [('add', moves_duplicated)]},
|
||||
context=context)
|
||||
|
||||
if duplicate_all:
|
||||
ctx = context and context.copy() or {}
|
||||
ctx['user'] = user
|
||||
packing_id = packing_obj.create(
|
||||
cursor, 0,
|
||||
{'planned_date': sale.sale_date,
|
||||
'customer': sale.party.id,
|
||||
'delivery_address': sale.packing_address.id,
|
||||
'reference': sale.reference,
|
||||
'warehouse': sale.warehouse.id,},
|
||||
context=ctx)
|
||||
default = {
|
||||
'state': 'draft',
|
||||
'packing_in': False,
|
||||
'packing_out': packing_id,
|
||||
'packing_in_return': False,
|
||||
'packing_out_return': False,
|
||||
'packing_internal': False,
|
||||
}
|
||||
move_obj.copy(
|
||||
cursor, user, duplicate_all, default=default, context=context)
|
||||
|
||||
workflow_service.trg_validate(0, 'stock.packing.out', packing_id,
|
||||
'waiting', cursor, context=ctx)
|
||||
|
||||
workflow_service.trg_validate(user, 'sale.sale', data['id'],
|
||||
'packing_ok', cursor, context=context)
|
||||
|
||||
HandlePackingException()
|
||||
@@ -11,6 +11,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="groups" eval="[('add', ref('group_sale'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_packing_handle_exception">
|
||||
<field name="name">Handle Packing Exception</field>
|
||||
<field name="wiz_name">sale.handle.packing.exception</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_view_form">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="type">form</field>
|
||||
@@ -79,9 +85,11 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<button name="invoice_ok" string="Ignore Invoice Exception"
|
||||
states="{'invisible': '''invoice_state != 'exception' or state == 'cancel' ''', 'readonly': '''%(group_sale)d not in groups'''}"
|
||||
icon="tryton-go-next"/>
|
||||
<button name="packing_ok" string="Ignore Packing Exception"
|
||||
states="{'invisible': '''packing_state != 'exception' or state == 'cancel' ''', 'readonly': '''%(group_sale)d not in groups'''}"
|
||||
icon="tryton-go-next"/>
|
||||
<button name="%(wizard_packing_handle_exception)d"
|
||||
type="action"
|
||||
string="Handle Packing Exception"
|
||||
states="{'invisible': '''packing_state != 'exception' or state == 'cancel' ''', 'readonly': '''%(group_sale)d not in groups'''}"
|
||||
icon="tryton-go-next"/>
|
||||
<button name="confirm" string="Confirm"
|
||||
states="{'invisible': '''state != 'quotation' ''', 'readonly': '''%(group_sale)d not in groups'''}"
|
||||
icon="tryton-go-next"/>
|
||||
@@ -103,8 +111,19 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="invoices" colspan="4" widget="one2many"/>
|
||||
</page>
|
||||
<page string="Packings" id="packings">
|
||||
<field name="moves" colspan="4" widget="one2many">
|
||||
<tree string="Moves">
|
||||
<field name="product"/>
|
||||
<field name="from_location"/>
|
||||
<field name="to_location"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="state"/>
|
||||
<field name="sale_exception_state"/>
|
||||
<field name="unit_digits" tree_invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="packings" colspan="4" widget="one2many"/>
|
||||
<field name="moves" colspan="4" widget="one2many"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="currency_digits" invisible="1" colspan="6"/>
|
||||
@@ -136,6 +155,30 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="handle_packing_exception_view_form">
|
||||
<field name="model">sale.handle.packing.exception.ask</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Handle packing Exception" col="2">
|
||||
<image name="tryton-dialog-information"/>
|
||||
<group col="1" colspan="1" id="moves">
|
||||
<separator string="Choose move to duplicate"
|
||||
id="choose"/>
|
||||
<field name="duplicate_moves">
|
||||
<tree string="Duplicate Moves" fill="1">
|
||||
<field name="product"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="unit_digits" tree_invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_sale_form">
|
||||
<field name="name">Sales</field>
|
||||
<field name="res_model">sale.sale</field>
|
||||
@@ -323,7 +366,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="name">Waiting Packing Invoice</field>
|
||||
<field name="workflow" ref="sale_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'packing_state': 'waiting'}) ignore_packing_exception create_packing()</field>
|
||||
<field name="action">write({'packing_state': 'waiting'}) create_packing()</field>
|
||||
</record>
|
||||
<record model="workflow.activity" id="sale_activity_packing_invoice_exception">
|
||||
<field name="name">Packing Invoice Exception</field>
|
||||
@@ -359,7 +402,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="name">Waiting Packing</field>
|
||||
<field name="workflow" ref="sale_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'packing_state': 'waiting'}) ignore_packing_exception() create_packing()</field>
|
||||
<field name="action">write({'packing_state': 'waiting'}) create_packing()</field>
|
||||
</record>
|
||||
<record model="workflow.activity" id="sale_activity_packing_exception">
|
||||
<field name="name">Packing Exception</field>
|
||||
|
||||
Reference in new issue
Block a user