diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/sale_stock/models/stock.py | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/sale_stock/models/stock.py')
| -rw-r--r-- | addons/sale_stock/models/stock.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py new file mode 100644 index 00000000..5450852a --- /dev/null +++ b/addons/sale_stock/models/stock.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from collections import defaultdict + +from odoo import api, fields, models, _ + + +class StockLocationRoute(models.Model): + _inherit = "stock.location.route" + sale_selectable = fields.Boolean("Selectable on Sales Order Line") + + +class StockMove(models.Model): + _inherit = "stock.move" + sale_line_id = fields.Many2one('sale.order.line', 'Sale Line', index=True) + + @api.model + def _prepare_merge_moves_distinct_fields(self): + distinct_fields = super(StockMove, self)._prepare_merge_moves_distinct_fields() + distinct_fields.append('sale_line_id') + return distinct_fields + + @api.model + def _prepare_merge_move_sort_method(self, move): + move.ensure_one() + keys_sorted = super(StockMove, self)._prepare_merge_move_sort_method(move) + keys_sorted.append(move.sale_line_id.id) + return keys_sorted + + def _get_related_invoices(self): + """ Overridden from stock_account to return the customer invoices + related to this stock move. + """ + rslt = super(StockMove, self)._get_related_invoices() + invoices = self.mapped('picking_id.sale_id.invoice_ids').filtered(lambda x: x.state == 'posted') + rslt += invoices + #rslt += invoices.mapped('reverse_entry_ids') + return rslt + + def _get_source_document(self): + res = super()._get_source_document() + return self.sale_line_id.order_id or res + + def _assign_picking_post_process(self, new=False): + super(StockMove, self)._assign_picking_post_process(new=new) + if new: + picking_id = self.mapped('picking_id') + sale_order_ids = self.mapped('sale_line_id.order_id') + for sale_order_id in sale_order_ids: + picking_id.message_post_with_view( + 'mail.message_origin_link', + values={'self': picking_id, 'origin': sale_order_id}, + subtype_id=self.env.ref('mail.mt_note').id) + + +class ProcurementGroup(models.Model): + _inherit = 'procurement.group' + + sale_id = fields.Many2one('sale.order', 'Sale Order') + + +class StockRule(models.Model): + _inherit = 'stock.rule' + + def _get_custom_move_fields(self): + fields = super(StockRule, self)._get_custom_move_fields() + fields += ['sale_line_id', 'partner_id'] + return fields + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + sale_id = fields.Many2one(related="group_id.sale_id", string="Sales Order", store=True, readonly=False) + + def _action_done(self): + res = super()._action_done() + sale_order_lines_vals = [] + for move in self.move_lines: + sale_order = move.picking_id.sale_id + # Creates new SO line only when pickings linked to a sale order and + # for moves with qty. done and not already linked to a SO line. + if not sale_order or move.location_dest_id.usage != 'customer' or move.sale_line_id or not move.quantity_done: + continue + product = move.product_id + so_line_vals = { + 'move_ids': [(4, move.id, 0)], + 'name': product.display_name, + 'order_id': sale_order.id, + 'product_id': product.id, + 'product_uom_qty': 0, + 'qty_delivered': move.quantity_done, + } + if product.invoice_policy == 'delivery': + # Check if there is already a SO line for this product to get + # back its unit price (in case it was manually updated). + so_line = sale_order.order_line.filtered(lambda sol: sol.product_id == product) + if so_line: + so_line_vals['price_unit'] = so_line[0].price_unit + elif product.invoice_policy == 'order': + # No unit price if the product is invoiced on the ordered qty. + so_line_vals['price_unit'] = 0 + sale_order_lines_vals.append(so_line_vals) + + if sale_order_lines_vals: + self.env['sale.order.line'].create(sale_order_lines_vals) + return res + + def _log_less_quantities_than_expected(self, moves): + """ Log an activity on sale order that are linked to moves. The + note summarize the real proccessed quantity and promote a + manual action. + + :param dict moves: a dict with a move as key and tuple with + new and old quantity as value. eg: {move_1 : (4, 5)} + """ + + def _keys_in_sorted(sale_line): + """ sort by order_id and the sale_person on the order """ + return (sale_line.order_id.id, sale_line.order_id.user_id.id) + + def _keys_in_groupby(sale_line): + """ group by order_id and the sale_person on the order """ + return (sale_line.order_id, sale_line.order_id.user_id) + + def _render_note_exception_quantity(moves_information): + """ Generate a note with the picking on which the action + occurred and a summary on impacted quantity that are + related to the sale order where the note will be logged. + + :param moves_information dict: + {'move_id': ['sale_order_line_id', (new_qty, old_qty)], ..} + + :return: an html string with all the information encoded. + :rtype: str + """ + origin_moves = self.env['stock.move'].browse([move.id for move_orig in moves_information.values() for move in move_orig[0]]) + origin_picking = origin_moves.mapped('picking_id') + values = { + 'origin_moves': origin_moves, + 'origin_picking': origin_picking, + 'moves_information': moves_information.values(), + } + return self.env.ref('sale_stock.exception_on_picking')._render(values=values) + + documents = self._log_activity_get_documents(moves, 'sale_line_id', 'DOWN', _keys_in_sorted, _keys_in_groupby) + self._log_activity(_render_note_exception_quantity, documents) + + return super(StockPicking, self)._log_less_quantities_than_expected(moves) + +class ProductionLot(models.Model): + _inherit = 'stock.production.lot' + + sale_order_ids = fields.Many2many('sale.order', string="Sales Orders", compute='_compute_sale_order_ids') + sale_order_count = fields.Integer('Sale order count', compute='_compute_sale_order_ids') + + @api.depends('name') + def _compute_sale_order_ids(self): + sale_orders = defaultdict(lambda: self.env['sale.order']) + for move_line in self.env['stock.move.line'].search([('lot_id', 'in', self.ids), ('state', '=', 'done')]): + move = move_line.move_id + if move.picking_id.location_dest_id.usage == 'customer' and move.sale_line_id.order_id: + sale_orders[move_line.lot_id.id] |= move.sale_line_id.order_id + for lot in self: + lot.sale_order_ids = sale_orders[lot.id] + lot.sale_order_count = len(lot.sale_order_ids) + + def action_view_so(self): + self.ensure_one() + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") + action['domain'] = [('id', 'in', self.mapped('sale_order_ids.id'))] + action['context'] = dict(self._context, create=False) + return action |
