summaryrefslogtreecommitdiff
path: root/addons/sale_stock/models/stock.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/sale_stock/models/stock.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/sale_stock/models/stock.py')
-rw-r--r--addons/sale_stock/models/stock.py174
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