mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-28 07:11:59 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,10 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.fields import Command
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
|
@ -20,14 +21,12 @@ class SaleOrder(models.Model):
|
|||
picking_policy = fields.Selection([
|
||||
('direct', 'As soon as possible'),
|
||||
('one', 'When all products are ready')],
|
||||
string='Shipping Policy', required=True, readonly=True, default='direct',
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
string='Shipping Policy', required=True, default='direct',
|
||||
help="If you deliver all products at once, the delivery order will be scheduled based on the greatest "
|
||||
"product lead time. Otherwise, it will be based on the shortest.")
|
||||
warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string='Warehouse', required=True,
|
||||
'stock.warehouse', string='Warehouse',
|
||||
compute='_compute_warehouse_id', store=True, readonly=False, precompute=True,
|
||||
states={'sale': [('readonly', True)], 'done': [('readonly', True)], 'cancel': [('readonly', False)]},
|
||||
check_company=True)
|
||||
picking_ids = fields.One2many('stock.picking', 'sale_id', string='Transfers')
|
||||
delivery_count = fields.Integer(string='Delivery Orders', compute='_compute_picking_ids')
|
||||
|
|
@ -36,8 +35,19 @@ class SaleOrder(models.Model):
|
|||
('started', 'Started'),
|
||||
('partial', 'Partially Delivered'),
|
||||
('full', 'Fully Delivered'),
|
||||
], string='Delivery Status', compute='_compute_delivery_status', store=True)
|
||||
procurement_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False)
|
||||
], string='Delivery Status', compute='_compute_delivery_status', store=True,
|
||||
help="Blue: Not Delivered/Started\n\
|
||||
Orange: Partially Delivered\n\
|
||||
Green: Fully Delivered")
|
||||
late_availability = fields.Boolean(
|
||||
string="Late Availability",
|
||||
compute='_compute_late_availability',
|
||||
search='_search_late_availability',
|
||||
help="True if any related picking has late availability"
|
||||
)
|
||||
stock_reference_ids = fields.Many2many(
|
||||
'stock.reference', 'stock_reference_sale_rel',
|
||||
'sale_id', 'reference_id', string='References', copy=False)
|
||||
effective_date = fields.Datetime("Effective Date", compute='_compute_effective_date', store=True, help="Completion date of the first delivery order.")
|
||||
expected_date = fields.Datetime( help="Delivery date you can promise to the customer, computed from the minimum lead time of "
|
||||
"the order lines in case of Service products. In case of shipping, the shipping policy of "
|
||||
|
|
@ -63,12 +73,12 @@ class SaleOrder(models.Model):
|
|||
UPDATE sale_order so
|
||||
SET warehouse_id = COALESCE(wh.id, %s)
|
||||
FROM stock_warehouse wh
|
||||
WHERE so.company_id = wh.company_id and so.warehouse_id IS NULL AND wh.active
|
||||
WHERE so.company_id = wh.company_id and so.warehouse_id IS NULL and wh.active
|
||||
"""
|
||||
params = [default_warehouse.id]
|
||||
|
||||
_logger.debug("Initializing column '%s' in table '%s'", column_name, self._table)
|
||||
self._cr.execute(query, params)
|
||||
self.env.cr.execute(query, params)
|
||||
|
||||
@api.depends('picking_ids.date_done')
|
||||
def _compute_effective_date(self):
|
||||
|
|
@ -96,24 +106,68 @@ class SaleOrder(models.Model):
|
|||
def _compute_expected_date(self):
|
||||
super(SaleOrder, self)._compute_expected_date()
|
||||
|
||||
@api.depends('picking_ids.products_availability_state')
|
||||
def _compute_late_availability(self):
|
||||
for order in self:
|
||||
order.late_availability = any(
|
||||
picking.products_availability_state == 'late' for picking in order.picking_ids
|
||||
)
|
||||
|
||||
def _search_late_availability(self, operator, value):
|
||||
if operator not in ('=', '!=') or not isinstance(value, bool):
|
||||
return NotImplemented
|
||||
|
||||
sub_query = self.env['stock.picking']._search([
|
||||
('sale_id', '!=', False), ('products_availability_state', operator, 'late')
|
||||
])
|
||||
return [('picking_ids', 'in', sub_query)]
|
||||
|
||||
def _select_expected_date(self, expected_dates):
|
||||
if self.picking_policy == "direct":
|
||||
return super()._select_expected_date(expected_dates)
|
||||
return max(expected_dates)
|
||||
|
||||
def write(self, values):
|
||||
@api.constrains('warehouse_id', 'state', 'order_line')
|
||||
def _check_warehouse(self):
|
||||
""" Ensure that the warehouse is set in case of storable products """
|
||||
orders_without_wh = self.filtered(lambda order: order.state not in ('draft', 'cancel') and not order.warehouse_id)
|
||||
company_ids_with_wh = {
|
||||
company_id.id for [company_id] in self.env['stock.warehouse']._read_group(
|
||||
domain=[('company_id', 'in', orders_without_wh.company_id.ids)],
|
||||
groupby=['company_id'],
|
||||
)
|
||||
}
|
||||
other_company = set()
|
||||
for order_line in orders_without_wh.order_line:
|
||||
if order_line.product_id.type != 'consu':
|
||||
continue
|
||||
if order_line.route_ids.company_id and order_line.route_ids.company_id != order_line.company_id:
|
||||
other_company.add(order_line.route_ids.company_id.id)
|
||||
continue
|
||||
if order_line.order_id.company_id.id in company_ids_with_wh:
|
||||
raise UserError(_('You must set a warehouse on your sale order to proceed.'))
|
||||
self.env['stock.warehouse'].with_company(order_line.order_id.company_id)._warehouse_redirect_warning()
|
||||
other_company_warehouses = self.env['stock.warehouse'].search([('company_id', 'in', list(other_company))])
|
||||
if any(c not in other_company_warehouses.company_id.ids for c in other_company):
|
||||
raise UserError(_("You must have a warehouse for line using a delivery in different company."))
|
||||
|
||||
def write(self, vals):
|
||||
values = vals
|
||||
if values.get('order_line') and self.state == 'sale':
|
||||
for order in self:
|
||||
pre_order_line_qty = {order_line: order_line.product_uom_qty for order_line in order.mapped('order_line') if not order_line.is_expense}
|
||||
|
||||
if values.get('partner_shipping_id'):
|
||||
if values.get('partner_shipping_id') and self.env.context.get('update_delivery_shipping_partner'):
|
||||
for order in self:
|
||||
order.picking_ids.partner_id = values.get('partner_shipping_id')
|
||||
elif values.get('partner_shipping_id'):
|
||||
new_partner = self.env['res.partner'].browse(values.get('partner_shipping_id'))
|
||||
for record in self:
|
||||
picking = record.mapped('picking_ids').filtered(lambda x: x.state not in ('done', 'cancel'))
|
||||
addresses = (record.partner_shipping_id.display_name, new_partner.display_name)
|
||||
message = _("""The delivery address has been changed on the Sales Order<br/>
|
||||
From <strong>"%s"</strong> To <strong>"%s"</strong>,
|
||||
You should probably update the partner on this document.""") % addresses
|
||||
From <strong>"%(old_address)s"</strong> to <strong>"%(new_address)s"</strong>,
|
||||
You should probably update the partner on this document.""",
|
||||
old_address=record.partner_shipping_id.display_name, new_address=new_partner.display_name)
|
||||
picking.activity_schedule('mail.mail_activity_data_warning', note=message, user_id=self.env.user.id)
|
||||
|
||||
if 'commitment_date' in values:
|
||||
|
|
@ -121,17 +175,20 @@ class SaleOrder(models.Model):
|
|||
# TODO: Log a note on each down document
|
||||
deadline_datetime = values.get('commitment_date')
|
||||
for order in self:
|
||||
order.order_line.move_ids.date_deadline = deadline_datetime or order.expected_date
|
||||
moves = order.order_line.move_ids.filtered(
|
||||
lambda m: m.state not in ('done', 'cancel') and m.location_dest_id.usage == 'customer'
|
||||
)
|
||||
moves.date_deadline = deadline_datetime or order.expected_date
|
||||
|
||||
res = super(SaleOrder, self).write(values)
|
||||
res = super().write(values)
|
||||
if values.get('order_line') and self.state == 'sale':
|
||||
rounding = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
for order in self:
|
||||
to_log = {}
|
||||
order.order_line.fetch(['product_uom_id', 'product_uom_qty', 'display_type', 'is_downpayment'])
|
||||
for order_line in order.order_line:
|
||||
if order_line.display_type:
|
||||
if order_line.display_type or order_line.is_downpayment:
|
||||
continue
|
||||
if float_compare(order_line.product_uom_qty, pre_order_line_qty.get(order_line, 0.0), precision_rounding=order_line.product_uom.rounding or rounding) < 0:
|
||||
if float_compare(order_line.product_uom_qty, pre_order_line_qty.get(order_line, 0.0), precision_rounding=order_line.product_uom_id.rounding) < 0:
|
||||
to_log[order_line] = (order_line.product_uom_qty, pre_order_line_qty.get(order_line, 0.0))
|
||||
if to_log:
|
||||
documents = self.env['stock.picking'].sudo()._log_activity_get_documents(to_log, 'move_ids', 'UP')
|
||||
|
|
@ -166,7 +223,7 @@ class SaleOrder(models.Model):
|
|||
def _compute_warehouse_id(self):
|
||||
for order in self:
|
||||
default_warehouse_id = self.env['ir.default'].with_company(
|
||||
order.company_id.id).get_model_defaults('sale.order').get('warehouse_id')
|
||||
order.company_id.id)._get_model_defaults('sale.order').get('warehouse_id')
|
||||
if order.state in ['draft', 'sent'] or not order.ids:
|
||||
# Should expect empty
|
||||
if default_warehouse_id is not None:
|
||||
|
|
@ -184,8 +241,8 @@ class SaleOrder(models.Model):
|
|||
res['warning'] = {
|
||||
'title': _('Warning!'),
|
||||
'message': _(
|
||||
'Do not forget to change the partner on the following delivery orders: %s'
|
||||
) % (','.join(pickings.mapped('name')))
|
||||
'Do not forget to change the partner on the following delivery orders: %s',
|
||||
','.join(pickings.mapped('name')))
|
||||
}
|
||||
return res
|
||||
|
||||
|
|
@ -232,12 +289,16 @@ class SaleOrder(models.Model):
|
|||
picking_id = picking_id[0]
|
||||
else:
|
||||
picking_id = pickings[0]
|
||||
action['context'] = dict(self._context, default_partner_id=self.partner_id.id, default_picking_type_id=picking_id.picking_type_id.id, default_origin=self.name, default_group_id=picking_id.group_id.id)
|
||||
action['context'] = dict(
|
||||
default_partner_id=self.partner_id.id,
|
||||
default_picking_type_id=picking_id.picking_type_id.id,
|
||||
)
|
||||
return action
|
||||
|
||||
def _prepare_invoice(self):
|
||||
invoice_vals = super(SaleOrder, self)._prepare_invoice()
|
||||
invoice_vals['invoice_incoterm_id'] = self.incoterm.id
|
||||
invoice_vals['delivery_date'] = self.effective_date
|
||||
return invoice_vals
|
||||
|
||||
def _log_decrease_ordered_quantity(self, documents, cancel=False):
|
||||
|
|
@ -258,3 +319,18 @@ class SaleOrder(models.Model):
|
|||
return self.env['ir.qweb']._render('sale_stock.exception_on_so', values)
|
||||
|
||||
self.env['stock.picking']._log_activity(_render_note_exception_quantity_so, documents)
|
||||
|
||||
def _is_display_stock_in_catalog(self):
|
||||
return True
|
||||
|
||||
# TODO: rename the parameter from reference to references in master for improved readability
|
||||
def _add_reference(self, reference):
|
||||
""" link the given references to the list of references. """
|
||||
self.ensure_one()
|
||||
self.stock_reference_ids = [Command.link(stock_reference.id) for stock_reference in reference]
|
||||
|
||||
# TODO: rename the parameter from reference to references in master for improved readability
|
||||
def _remove_reference(self, reference):
|
||||
""" remove the given references from the list of references. """
|
||||
self.ensure_one()
|
||||
self.stock_reference_ids = [Command.unlink(stock_reference.id) for stock_reference in reference]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue