from odoo import fields, models class SaleOrder(models.Model): _inherit = 'sale.order' order_promotion_ids = fields.One2many('sale.order.promotion', 'order_id', 'Promotions') def add_free_product(self, promotions): for promotion in promotions: program_line = self.env['promotion.program.line'].browse(promotion['program_line_id']) for free_product in program_line.free_product_ids: if free_product.product_id.merchandise_ok: self.env['sale.order.line'].create({ 'order_id': self.id, 'name': f"Free Product {free_product.product_id.display_name} Quantity ({free_product.qty})", 'display_type': 'line_note' }) def apply_promotion_program(self): userdata = { 'user_id': self.partner_id.user_id.id, 'partner_id': self.partner_id.id } order_lines = [] iu_products = [] # Internal Use products for line in self.order_promotion_ids: promotion = line.program_line_id.format(user=userdata, qty=line.quantity) products = promotion['products'] free_products = promotion['free_products'] all_products = products.copy() if promotion['promotion_type']['value'] == 'merchandise': iu_products += filter(lambda x: x['qty'] > 0, free_products) else: all_products = self._merge_promotion_products(promotion) promotion_price = promotion['price']['price_discount'] * line.quantity * 1.11 promotion_amt_total = sum(product['price']['price'] * product['qty'] for product in all_products) promotion_used_price = 0 for index, product in enumerate(all_products): order_line_default = { 'company_id': 1, 'order_id': self.id, 'order_promotion_id': line.id, 'product_id': product['id'], 'product_uom_qty': product['qty'], } if promotion['promotion_type']['value'] == 'special_price': order_lines.append({ **order_line_default, 'price_unit': product['price']['price'], 'discount': product['price']['discount_percentage'] }) continue product_price = product['price']['price'] contrib_decimal = product_price * product['qty'] / promotion_amt_total if index < len(all_products) - 1: contrib_price = contrib_decimal * promotion_price else: contrib_price = promotion_price - promotion_used_price contrib_price_unit = contrib_price / product['qty'] contrib_disc_unit = round((product_price - contrib_price_unit) / product_price * 100, 2) product_subtotal = round((100 - contrib_disc_unit) / 100 * product_price, 2) * product['qty'] promotion_used_price += product_subtotal order_lines.append({ **order_line_default, 'price_unit': product_price, 'discount': contrib_disc_unit }) self.env['sale.order.line'].create(order_lines) self._create_promotion_program_iu_docs(iu_products) def _merge_promotion_products(self, promotion): """ Merges the products and free products from a promotion into a single dictionary. Parameters: promotion (dict): A dictionary representing a promotion with 'products' and 'free_products' keys. Returns: list: A list containing the merged products from the promotion. """ merged_products = {} for product in promotion['products'] + promotion['free_products']: product_id = product['id'] product_qty = product['qty'] if product_id in merged_products: merged_products[product_id]['qty'] += product_qty else: merged_products[product_id] = product return list(merged_products.values()) def _create_promotion_program_iu_docs(self, products): if len(products) == 0: return False default = { 'picking_type_id': 33, # PT Indoteknik (Bandengan): Internal Transfers 'location_id': 57, # BU/Stock 'location_dest_id': 49, # Virtual Locations/Internal Use 'account_id': 596, # Biaya awareness 'product_uom': 1 # Unit } picking_type = self.env['stock.picking.type'].browse(default['picking_type_id']) picking = self.env['stock.picking'].create({ 'name': picking_type.sequence_id.next_by_id(), 'picking_type_id': default['picking_type_id'], 'partner_id': self.partner_id.id, 'real_shipping_id': self.real_shipping_id.id, 'location_id': default['location_id'], 'location_dest_id': default['location_dest_id'], 'account_id': default['account_id'], 'origin': self.display_name, 'is_internal_use': True }) for product in products: picking.move_ids_without_package.create({ 'product_id': product['id'], 'name': product['display_name'], 'product_uom_qty': product['qty'], 'product_uom': default['product_uom'], 'location_id': default['location_id'], 'location_dest_id': default['location_dest_id'], 'picking_id': picking.id }) self.picking_iu_id = picking.id