mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 17:12:00 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,4 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import project_project
|
||||
from . import sale_order_line
|
||||
from . import stock_move
|
||||
from . import stock_picking
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ProjectProject(models.Model):
|
||||
_inherit = 'project.project'
|
||||
|
||||
def _get_picking_action(self, action_name, picking_type=None):
|
||||
result = super()._get_picking_action(action_name, picking_type)
|
||||
|
||||
if picking_type and (property_warehouse := self.env.user.property_warehouse_id):
|
||||
if picking_type == 'outgoing':
|
||||
result['context']['default_picking_type_id'] = property_warehouse.out_type_id.id
|
||||
elif picking_type == 'incoming':
|
||||
result['context']['default_picking_type_id'] = property_warehouse.in_type_id.id
|
||||
|
||||
return result
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
|
|
@ -14,9 +15,9 @@ class SaleOrderLine(models.Model):
|
|||
action_per_sol = super()._get_action_per_item()
|
||||
stock_move_action = self.env.ref('sale_project_stock.stock_move_per_sale_order_line_action').id
|
||||
stock_move_ids_per_sol = {}
|
||||
if self.user_has_groups('stock.group_stock_user'):
|
||||
stock_move_read_group = self.env['stock.move']._read_group([('sale_line_id', 'in', self.ids)], ['sale_line_id', 'ids:array_agg(id)'], ['sale_line_id'])
|
||||
stock_move_ids_per_sol = {res['sale_line_id'][0]: res['ids'] for res in stock_move_read_group}
|
||||
if self.env.user.has_group('stock.group_stock_user'):
|
||||
stock_move_read_group = self.env['stock.move']._read_group([('sale_line_id', 'in', self.ids)], ['sale_line_id'], ['id:array_agg'])
|
||||
stock_move_ids_per_sol = {sale_line.id: ids for sale_line, ids in stock_move_read_group}
|
||||
for sol in self:
|
||||
stock_move_ids = stock_move_ids_per_sol.get(sol.id, [])
|
||||
if not sol.is_service and stock_move_ids:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = 'stock.move'
|
||||
|
||||
def _sale_get_invoice_price(self, order):
|
||||
""" Based on the current stock move, compute the price to reinvoice the analytic line that is going to be created (so the
|
||||
price of the sale line).
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if self.product_id.expense_policy == 'sales_price':
|
||||
return order.pricelist_id._get_product_price(
|
||||
self.product_id,
|
||||
1.0,
|
||||
uom=self.product_uom,
|
||||
date=order.date_order,
|
||||
)
|
||||
|
||||
uom_precision_digits = self.env['decimal.precision'].precision_get('Product Unit')
|
||||
if float_is_zero(self.quantity, precision_digits=uom_precision_digits):
|
||||
return 0.0
|
||||
|
||||
price_unit = self.product_id.standard_price
|
||||
# Prevent unnecessary currency conversion that could be impacted by exchange rate
|
||||
# fluctuations
|
||||
if self.company_id.currency_id and price_unit and self.company_id.currency_id == order.currency_id:
|
||||
return self.company_id.currency_id.round(price_unit)
|
||||
|
||||
currency_id = self.company_id.currency_id
|
||||
if currency_id and currency_id != order.currency_id:
|
||||
price_unit = currency_id._convert(price_unit, order.currency_id, order.company_id, order.date_order or fields.Date.today())
|
||||
return price_unit
|
||||
|
||||
def _sale_prepare_sale_line_values(self, order, price, last_sequence):
|
||||
""" Generate the sale.line creation value from the current stock move """
|
||||
self.ensure_one()
|
||||
|
||||
order = order.sudo()
|
||||
fpos = order.fiscal_position_id or order.fiscal_position_id._get_fiscal_position(order.partner_id)
|
||||
product_taxes = self.product_id.sudo().taxes_id._filter_taxes_by_company(order.company_id)
|
||||
taxes = fpos.map_tax(product_taxes)
|
||||
|
||||
return {
|
||||
'order_id': order.id,
|
||||
'name': self.reference,
|
||||
'sequence': last_sequence,
|
||||
'price_unit': price,
|
||||
'tax_ids': [x.id for x in taxes],
|
||||
'discount': 0.0,
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom_qty': self.product_uom_qty,
|
||||
'qty_delivered': self.quantity,
|
||||
}
|
||||
|
||||
def _get_new_picking_values(self):
|
||||
return {
|
||||
**super()._get_new_picking_values(),
|
||||
'project_id': self.sale_line_id.order_id.project_id.id,
|
||||
}
|
||||
|
||||
def _assign_picking_values(self, picking):
|
||||
return {
|
||||
**super()._assign_picking_values(picking),
|
||||
'project_id': self[:1].sale_line_id.order_id.project_id.id,
|
||||
}
|
||||
|
||||
def _prepare_procurement_values(self):
|
||||
res = super()._prepare_procurement_values()
|
||||
project = self.sale_line_id.order_id.project_id
|
||||
if project:
|
||||
res['project_id'] = project.id
|
||||
return res
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
def button_validate(self):
|
||||
res = super().button_validate()
|
||||
if res is not True:
|
||||
return res
|
||||
|
||||
for picking in self:
|
||||
project = picking.project_id
|
||||
sale_order = project.sudo().reinvoiced_sale_order_id
|
||||
if not (sale_order and picking.picking_type_id.analytic_costs):
|
||||
continue
|
||||
reinvoicable_stock_moves = picking.move_ids.filtered(lambda m: m.product_id.expense_policy in {'sales_price', 'cost'})
|
||||
if not reinvoicable_stock_moves:
|
||||
continue
|
||||
# raise if the sale order is not currently open
|
||||
if sale_order.state in ('draft', 'sent'):
|
||||
raise UserError(_(
|
||||
"The Sales Order %(order)s linked to the Project %(project)s must be"
|
||||
" validated before validating the stock picking.",
|
||||
order=sale_order.name,
|
||||
project=project.name,
|
||||
))
|
||||
elif sale_order.state == 'cancel':
|
||||
raise UserError(_(
|
||||
"The Sales Order %(order)s linked to the Project %(project)s is cancelled."
|
||||
" You cannot validate a stock picking on a cancelled Sales Order.",
|
||||
order=sale_order.name,
|
||||
project=project.name,
|
||||
))
|
||||
elif sale_order.locked:
|
||||
raise UserError(_(
|
||||
"The Sales Order %(order)s linked to the Project %(project)s is currently locked."
|
||||
" You cannot validate a stock picking on a locked Sales Order."
|
||||
" Please create a new SO linked to this Project.",
|
||||
order=sale_order.name,
|
||||
project=project.name,
|
||||
))
|
||||
# Create SOLs in reinvoiced_sale_order_id with reinvoicable stock moves
|
||||
sale_line_values_to_create = []
|
||||
# Get last sequence SOL
|
||||
last_so_line = self.env['sale.order.line'].search_read(
|
||||
[('order_id', '=', sale_order.id)],
|
||||
['sequence'], order='sequence desc', limit=1,
|
||||
)
|
||||
last_sequence = next((sol['sequence'] for sol in last_so_line), 100)
|
||||
|
||||
for stock_move in reinvoicable_stock_moves:
|
||||
# Get price
|
||||
price = stock_move._sale_get_invoice_price(sale_order)
|
||||
# Create the sale lines in batch
|
||||
sale_line_values_to_create.append(stock_move._sale_prepare_sale_line_values(sale_order, price, last_sequence))
|
||||
last_sequence += 1
|
||||
self.env['sale.order.line'].with_context(skip_procurement=True).sudo().create(sale_line_values_to_create)
|
||||
return res
|
||||
Loading…
Add table
Add a link
Reference in a new issue