19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:47 +01:00
parent accf5918df
commit 6e65e8c877
688 changed files with 225434 additions and 199401 deletions

View file

@ -6,5 +6,6 @@ from . import stock_picking
from . import res_company
from . import stock_warehouse
from . import purchase
from . import stock_replenish_mixin
from . import stock_rule
from . import stock_orderpoint

View file

@ -8,14 +8,20 @@ from odoo.exceptions import UserError
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
default_location_dest_id_is_subcontracting_loc = fields.Boolean(related='picking_type_id.default_location_dest_id.is_subcontracting_location')
default_location_dest_id_is_subcontracting_loc = fields.Boolean(compute='_compute_default_location_dest_id_is_subcontracting_loc')
@api.depends('picking_type_id.default_location_dest_id')
def _compute_default_location_dest_id_is_subcontracting_loc(self):
for order in self:
order.default_location_dest_id_is_subcontracting_loc = order.picking_type_id.default_location_dest_id.is_subcontract()
@api.depends('default_location_dest_id_is_subcontracting_loc')
def _compute_dest_address_id(self):
dropship_subcontract_pos = self.filtered(lambda po: po.default_location_dest_id_is_subcontracting_loc)
for order in dropship_subcontract_pos:
subcontractor_ids = order.picking_type_id.default_location_dest_id.subcontractor_ids
order.dest_address_id = subcontractor_ids if len(subcontractor_ids) == 1 else False
if len(subcontractor_ids) == 1:
order.dest_address_id = subcontractor_ids
super(PurchaseOrder, self - dropship_subcontract_pos)._compute_dest_address_id()
@api.onchange('picking_type_id')

View file

@ -33,7 +33,7 @@ class ResCompany(models.Model):
'company_id': company.id,
'warehouse_id': False,
'sequence_id': sequence.id,
'code': 'incoming',
'code': 'dropship',
'default_location_src_id': self.env.ref('stock.stock_location_suppliers').id,
'default_location_dest_id': company.subcontracting_location_id.id,
'sequence_code': 'DSC',
@ -45,7 +45,7 @@ class ResCompany(models.Model):
pick_type.company_id.dropship_subcontractor_pick_type_id = pick_type.id
def _create_subcontracting_dropshipping_rules(self):
route = self.env.ref('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping')
dropship_route = self.env.ref('stock_dropshipping.route_drop_shipping')
supplier_location = self.env.ref('stock.stock_location_suppliers')
vals = []
for company in self:
@ -62,7 +62,7 @@ class ResCompany(models.Model):
'location_dest_id': subcontracting_location.id,
'location_src_id': supplier_location.id,
'procure_method': 'make_to_stock',
'route_id': route.id,
'route_id': dropship_route.id,
'picking_type_id': dropship_picking_type.id,
'company_id': company.id,
})
@ -71,10 +71,8 @@ class ResCompany(models.Model):
@api.model
def _create_missing_subcontracting_dropshipping_rules(self):
route = self.env.ref('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping')
company_ids = self.env['res.company'].search([])
company_has_rules = self.env['stock.rule'].search([('route_id', '=', route.id)]).mapped('company_id')
company_todo_rules = company_ids - company_has_rules
route = self.env.ref('stock_dropshipping.route_drop_shipping')
company_todo_rules = self.env['stock.rule'].search([('route_id', '=', route.id)]).mapped('company_id')
company_todo_rules._create_subcontracting_dropshipping_rules()
@api.model

View file

@ -7,23 +7,31 @@ from odoo import models
class StockMove(models.Model):
_inherit = "stock.move"
def _prepare_procurement_values(self):
vals = super()._prepare_procurement_values()
partner = self.group_id.partner_id
if not vals.get('partner_id') and partner and self.location_id.is_subcontracting_location:
vals['partner_id'] = partner.id
return vals
def _is_purchase_return(self):
res = super()._is_purchase_return()
return res or self._is_dropshipped_returned()
def _is_dropshipped(self):
res = super()._is_dropshipped()
return res or (
return (
res
# subcontractor -> customer
or (
self.partner_id.property_stock_subcontractor.parent_path
and self.partner_id.property_stock_subcontractor.parent_path in self.location_id.parent_path
and self.location_dest_id.usage == 'customer'
and (
(
self.partner_id.property_stock_subcontractor.parent_path
in self.location_id.parent_path
and self.location_dest_id.usage == "customer"
)
# Vendor -> subcontractor location
or (
self.partner_id.property_stock_subcontractor.parent_path
in self.location_dest_id.parent_path
and self.location_id.usage == "supplier"
)
)
)
)
def _is_dropshipped_returned(self):

View file

@ -7,8 +7,8 @@ from odoo import models
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
def _prepare_procurement_values(self, date=False, group=False):
vals = super()._prepare_procurement_values(date, group)
if not vals.get('partner_id') and self.location_id.is_subcontracting_location and len(self.location_id.subcontractor_ids) == 1:
def _prepare_procurement_values(self, date=False):
vals = super()._prepare_procurement_values(date)
if not vals.get('partner_id') and self.location_id.is_subcontract() and len(self.location_id.subcontractor_ids) == 1:
vals['partner_id'] = self.location_id.subcontractor_ids.id
return vals

View file

@ -9,64 +9,20 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
def _compute_is_dropship(self):
dropship_subcontract_pickings = self.filtered(lambda p: p.location_dest_id.is_subcontracting_location and p.location_id.usage == 'supplier')
dropship_subcontract_pickings = self.filtered(lambda p: p.location_dest_id.is_subcontract() and p.location_id.usage == 'supplier')
dropship_subcontract_pickings.is_dropship = True
super(StockPicking, self - dropship_subcontract_pickings)._compute_is_dropship()
def _get_warehouse(self, subcontract_move):
if subcontract_move.sale_line_id:
return subcontract_move.sale_line_id.order_id.warehouse_id
return super(StockPicking, self)._get_warehouse(subcontract_move)
def _action_done(self):
res = super()._action_done()
# If needed, create a compensation layer, so we add the MO cost to the dropship one
svls = self.env['stock.valuation.layer']
for move in self.move_ids:
if not (move.is_subcontract and move._is_dropshipped() and move.state == 'done'):
continue
dropship_svls = move.stock_valuation_layer_ids
if not dropship_svls:
continue
# In a backorder chain, only generate SVLs for the latest backorder, or else their
# value will be cumulative.
if any(move.move_orig_ids.production_id.mapped('backorder_sequence')):
moves_with_svls = move.move_orig_ids.filtered('stock_valuation_layer_ids')
subcontract_svls = max(
moves_with_svls,
key=lambda sm: sm.production_id.backorder_sequence
).stock_valuation_layer_ids
else:
subcontract_svls = move.move_orig_ids.stock_valuation_layer_ids
subcontract_value = sum(subcontract_svls.mapped('value'))
dropship_value = abs(sum(dropship_svls.mapped('value')))
diff = subcontract_value - dropship_value
if float_compare(diff, 0, precision_rounding=move.company_id.currency_id.rounding) <= 0:
continue
svl_vals = move._prepare_common_svl_vals()
svl_vals.update({
'remaining_value': 0,
'remaining_qty': 0,
'value': -diff,
'quantity': 0,
'unit_cost': 0,
'stock_valuation_layer_id': dropship_svls[0].id,
'stock_move_id': move.id,
})
svls |= self.env['stock.valuation.layer'].create(svl_vals)
svls._validate_accounting_entries()
return res
return super()._get_warehouse(subcontract_move)
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
res = super()._prepare_subcontract_mo_vals(subcontract_move, bom)
if not res.get('picking_type_id') and (
subcontract_move.location_dest_id.usage == 'customer'
or subcontract_move.location_dest_id.is_subcontracting_location
or subcontract_move.location_dest_id.is_subcontract()
):
# If the if-condition is respected, it means that `subcontract_move` is not
# related to a specific warehouse. This can happen if, for instance, the user

View file

@ -0,0 +1,12 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.fields import Domain
class StockReplenishMixin(models.AbstractModel):
_inherit = 'stock.replenish.mixin'
def _get_allowed_route_domain(self):
domains = super()._get_allowed_route_domain()
return Domain.AND([domains, [('id', '!=', self.env.ref('stock_dropshipping.route_drop_shipping', raise_if_not_found=False).id)]])

View file

@ -8,14 +8,16 @@ class StockRule(models.Model):
_inherit = 'stock.rule'
def _prepare_purchase_order(self, company_id, origins, values):
if 'partner_id' not in values[0] \
if not values[0].get('partner_id') \
and (company_id.subcontracting_location_id.parent_path in self.location_dest_id.parent_path
or self.location_dest_id.is_subcontracting_location):
values[0]['partner_id'] = values[0]['group_id'].partner_id.id
or self.location_dest_id.is_subcontract()):
move = values[0].get('move_dest_ids')
if move and move.raw_material_production_id.subcontractor_id:
values[0]['partner_id'] = move.raw_material_production_id.subcontractor_id.id
return super()._prepare_purchase_order(company_id, origins, values)
def _make_po_get_domain(self, company_id, values, partner):
domain = super()._make_po_get_domain(company_id, values, partner)
if values.get('partner_id', False):
if self.location_src_id.usage == 'supplier' and self.location_dest_id.is_subcontract() and values.get('partner_id', False):
domain += (('dest_address_id', '=', values.get('partner_id')),)
return domain

View file

@ -7,27 +7,23 @@ from odoo import api, fields, models, _
class StockWarehouse(models.Model):
_inherit = 'stock.warehouse'
subcontracting_dropshipping_to_resupply = fields.Boolean(
'Dropship Subcontractors', default=True,
help="Dropship subcontractors with components")
subcontracting_dropshipping_pull_id = fields.Many2one(
'stock.rule', 'Subcontracting-Dropshipping MTS Rule'
'stock.rule', 'Subcontracting-Dropshipping MTS Rule', copy=False
)
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
# if new warehouse has resupply enabled, enable global route
if any([vals.get('subcontracting_dropshipping_to_resupply', False) for vals in vals_list]):
if any(vals.get('subcontracting_to_resupply', False) for vals in vals_list):
res.update_global_route_dropship_subcontractor()
return res
def write(self, vals):
res = super().write(vals)
# if all warehouses have resupply disabled, disable global route, until its enabled on a warehouse
if 'subcontracting_dropshipping_to_resupply' in vals or 'active' in vals:
if 'subcontracting_dropshipping_to_resupply' in vals:
if 'subcontracting_to_resupply' in vals or 'active' in vals:
if 'subcontracting_to_resupply' in vals:
# ignore when warehouse archived since it will auto-archive all of its rules
self._update_dropship_subcontract_rules()
self.update_global_route_dropship_subcontractor()
@ -36,9 +32,9 @@ class StockWarehouse(models.Model):
def _update_dropship_subcontract_rules(self):
'''update (archive/unarchive) any warehouse subcontracting location dropship rules'''
subcontracting_locations = self._get_subcontracting_locations()
route_id = self._find_or_create_global_route('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping',
route_id = self._find_or_create_global_route('stock_dropshipping.route_drop_shipping',
_('Dropship Subcontractor on Order'))
warehouses_dropship = self.filtered(lambda w: w.subcontracting_dropshipping_to_resupply and w.active)
warehouses_dropship = self.filtered(lambda w: w.subcontracting_to_resupply and w.active)
if warehouses_dropship:
self.env['stock.rule'].with_context(active_test=False).search([
('route_id', '=', route_id.id),
@ -55,7 +51,7 @@ class StockWarehouse(models.Model):
('location_src_id', 'in', subcontracting_locations.ids)]).action_archive()
def update_global_route_dropship_subcontractor(self):
route_id = self._find_or_create_global_route('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping',
route_id = self._find_or_create_global_route('stock_dropshipping.route_drop_shipping',
_('Dropship Subcontractor on Order'))
# if route has no pull rules, it means all warehouses have Dropship Subcontractor disabled
# Pick type is per company so we need to check rules per company to archive it, however
@ -67,27 +63,26 @@ class StockWarehouse(models.Model):
route_id.active = bool(all_rules.filtered(lambda r: r.action == 'pull'))
def _get_global_route_rules_values(self):
rules = super()._get_global_route_rules_values()
def _generate_global_route_rules_values(self):
rules = super()._generate_global_route_rules_values()
subcontract_location_id = self._get_subcontracting_location()
production_location_id = self._get_production_location()
rules.update({
'subcontracting_dropshipping_pull_id': {
'depends': ['subcontracting_dropshipping_to_resupply'],
'depends': ['subcontracting_to_resupply'],
'create_values': {
'procure_method': 'make_to_order',
'company_id': self.company_id.id,
'action': 'pull',
'auto': 'manual',
'route_id': self._find_or_create_global_route('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping',
_('Dropship Subcontractor on Order')).id,
'route_id': self._find_or_create_global_route('stock_dropshipping.route_drop_shipping', self.env._('Dropship Subcontractor on Order')).id,
'name': self._format_rulename(subcontract_location_id, production_location_id, False),
'location_dest_id': production_location_id.id,
'location_src_id': subcontract_location_id.id,
'picking_type_id': self.subcontracting_type_id.id
},
'update_values': {
'active': self.subcontracting_dropshipping_to_resupply
'active': self.subcontracting_to_resupply
}
},
})