-
Notifications
You must be signed in to change notification settings - Fork 28
/
purchase_order_line.py
190 lines (175 loc) · 7.54 KB
/
purchase_order_line.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
##############################################################################
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from odoo.tools import float_compare
import json
from lxml import etree
import logging
_logger = logging.getLogger(__name__)
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
delivery_status = fields.Selection([
('no', 'Not purchased'),
('to receive', 'To Receive'),
('received', 'Received'),
],
string='Delivery Status',
compute='_compute_delivery_status',
store=True,
readonly=True,
copy=False,
default='no'
)
vouchers = fields.Char(
compute='_compute_vouchers'
)
qty_on_voucher = fields.Float(
compute="_compute_qty_on_voucher",
string="On Voucher",
digits='Product Unit of Measure',
)
qty_returned = fields.Float(
string='Returned',
copy=False,
default=0.0,
readonly=True,
compute='_compute_qty_returned'
)
@api.depends_context('voucher')
def _compute_qty_on_voucher(self):
# al calcular por voucher no tenemos en cuenta el metodo de facturacion
# es decir, que calculamos como si fuese metodo segun lo recibido
voucher = self._context.get('voucher', False)
if not voucher:
self.update({'qty_on_voucher': 0.0})
return
lines = self.filtered(
lambda x: x.order_id.state in ['purchase', 'done'])
moves = self.env['stock.move'].search([
('id', 'in', lines.mapped('move_ids').ids),
('state', '=', 'done'),
('picking_id.vouchers', 'ilike', voucher[0]),
])
for line in lines:
line.qty_on_voucher = sum(moves.filtered(
lambda x: x.id in line.move_ids.ids).mapped('product_uom_qty'))
def button_cancel_remaining(self):
# la cancelación de kits no está bien resuelta ya que odoo
# solo computa la cantidad entregada cuando todo el kit se entregó.
# Cuestión que, por ahora, desactivamos la cancelación de kits.
bom_enable = 'bom_ids' in self.env['product.template']._fields
for rec in self:
old_product_qty = rec.product_qty
# TODO tal vez cambiar en v10
# en este caso si lo bloqueamos ya que si llegan a querer generar
# nc lo pueden hacer con el buscar líneas de las facturas
# y luego lo pueden terminar cancelando
if rec.qty_invoiced > rec.qty_received:
raise UserError(_(
'You can not cancel remianing qty to receive because '
'there are more product invoiced than the received. '
'You should correct invoice or ask for a refund'))
if bom_enable:
bom = self.env['mrp.bom']._bom_find(
products=rec.product_id)[rec.product_id]
if bom and bom.type == 'phantom':
raise UserError(_(
"Cancel remaining can't be called for Kit Products "
"(products with a bom of type kit)."))
rec.product_qty = rec.qty_received
to_cancel_moves = rec.move_ids.filtered(
lambda x: x.state not in ['done', 'cancel'])
to_cancel_moves._cancel_quantity()
rec.order_id.message_post(
body=_(
'Cancel remaining call for line "%s" (id %s), line '
'qty updated from %s to %s') % (
rec.name, rec.id, old_product_qty, rec.product_qty))
def _compute_vouchers(self):
for rec in self:
rec.vouchers = ', '.join(rec.mapped(
'move_ids.picking_id.voucher_ids.display_name'))
@api.depends(
'order_id.state', 'qty_received', 'qty_returned', 'product_qty',
'order_id.force_delivered_status')
def _compute_delivery_status(self):
precision = self.env['decimal.precision'].precision_get(
'Product Unit of Measure')
for line in self:
if line.state not in ('purchase', 'done'):
line.delivery_status = 'no'
continue
if line.order_id.force_delivered_status:
line.delivery_status = line.order_id.force_delivered_status
continue
if float_compare(
(line.qty_received + line.qty_returned), line.product_qty,
precision_digits=precision) == -1:
line.delivery_status = 'to receive'
elif float_compare(
(line.qty_received + line.qty_returned), line.product_qty,
precision_digits=precision) >= 0:
line.delivery_status = 'received'
else:
line.delivery_status = 'no'
@api.onchange('product_qty')
def _onchange_product_qty(self):
if (
self.state == 'purchase' and
self.product_id.type in ['product', 'consu'] and
self.product_qty < self._origin.product_qty):
warning_mess = {
'title': _('Ordered quantity decreased!'),
'message': (
'¡Está reduciendo la cantidad pedida! Recomendamos usar'
' el botón para cancelar remanente y'
' luego setear la cantidad deseada.'),
}
self.product_qty = self._origin.product_qty
return {'warning': warning_mess}
return {}
@api.depends('order_id.state', 'move_ids.state')
def _compute_qty_returned(self):
for line in self:
qty = 0.0
for move in line.move_ids.filtered(
lambda m: m.state == 'done' and
m.location_id.usage != 'supplier' and m.to_refund):
qty += move.product_uom._compute_quantity(
move.product_uom_qty,
line.product_uom)
line.qty_returned = qty
# Overwrite the origin method to introduce the qty_on_voucher
def action_add_all_to_invoice(self):
for rec in self:
rec.invoice_qty = rec.qty_on_voucher or (
rec.qty_to_invoice + rec.invoice_qty)
@api.model
def fields_view_get(self, view_id=None, view_type='form',
toolbar=False, submenu=False):
"""
If we came from invoice, we send in context 'force_line_edit'
and we change tree view to make editable and also field qty
"""
res = super().fields_view_get(
view_id=view_id, view_type=view_type,
toolbar=toolbar, submenu=submenu)
if self._context.get('force_line_edit') and view_type == 'tree':
doc = etree.XML(res['arch'])
placeholder = doc.xpath("//field[1]")[0]
placeholder.addprevious(
etree.Element('field', {
'name': 'qty_on_voucher',
}))
# make all fields not editable
node = doc.xpath("//field[1]")[0]
node.set('readonly', '1')
modifiers = json.loads(node.get("modifiers") or "{}")
modifiers['readonly'] = True
node.set("modifiers", json.dumps(modifiers))
res['fields'].update(self.fields_get(['qty_on_voucher']))
res['arch'] = etree.tostring(doc)
return res