summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models/promotion
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2023-09-23 11:50:20 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2023-09-23 11:50:20 +0700
commitdae4a3bf266ba4c19b1ba1d11c52ed9e19259b7c (patch)
tree8858e0660aa004b582aedf6f437c0f455f8ad1ef /indoteknik_custom/models/promotion
parentadbc9b985f1c5fb2b2f41f79c686b3a573003e62 (diff)
Update promotion program scheme
Diffstat (limited to 'indoteknik_custom/models/promotion')
-rw-r--r--indoteknik_custom/models/promotion/__init__.py7
-rw-r--r--indoteknik_custom/models/promotion/promotion_free_product.py29
-rw-r--r--indoteknik_custom/models/promotion/promotion_product.py29
-rw-r--r--indoteknik_custom/models/promotion/promotion_program.py18
-rw-r--r--indoteknik_custom/models/promotion/promotion_program_line.py114
-rw-r--r--indoteknik_custom/models/promotion/sale_order.py110
-rw-r--r--indoteknik_custom/models/promotion/sale_order_line.py7
-rw-r--r--indoteknik_custom/models/promotion/sale_order_promotion.py15
8 files changed, 329 insertions, 0 deletions
diff --git a/indoteknik_custom/models/promotion/__init__.py b/indoteknik_custom/models/promotion/__init__.py
new file mode 100644
index 00000000..631df8f3
--- /dev/null
+++ b/indoteknik_custom/models/promotion/__init__.py
@@ -0,0 +1,7 @@
+from . import promotion_program
+from . import promotion_program_line
+from . import promotion_product
+from . import promotion_free_product
+from . import sale_order_promotion
+from . import sale_order_line
+from . import sale_order \ No newline at end of file
diff --git a/indoteknik_custom/models/promotion/promotion_free_product.py b/indoteknik_custom/models/promotion/promotion_free_product.py
new file mode 100644
index 00000000..c5055562
--- /dev/null
+++ b/indoteknik_custom/models/promotion/promotion_free_product.py
@@ -0,0 +1,29 @@
+from odoo import fields, models
+
+
+class PromotionFreeProduct(models.Model):
+ _name = "promotion.free_product"
+ _rec_name = "product_id"
+
+ product_id = fields.Many2one(comodel_name="product.product", string="Product variant")
+ qty = fields.Integer(string="Quantity")
+ program_line_id = fields.Many2one(comodel_name="promotion.program.line", string="Program line")
+
+ def formats(self, purchase_qty=1):
+ ir_attachment = self.env['ir.attachment']
+
+ result = []
+ for rec in self:
+ weight = rec.product_id.weight or 0
+ result.append({
+ 'id': rec.product_id.id,
+ 'image': ir_attachment.api_image('product.template', 'image_256', rec.product_id.product_tmpl_id.id),
+ 'display_name': rec.product_id.display_name,
+ 'name': rec.product_id.name,
+ 'code': rec.product_id.code,
+ 'price': rec.product_id.calculate_website_price(),
+ 'qty': rec.qty * purchase_qty,
+ 'weight': weight,
+ 'package_weight': weight * rec.qty
+ })
+ return result \ No newline at end of file
diff --git a/indoteknik_custom/models/promotion/promotion_product.py b/indoteknik_custom/models/promotion/promotion_product.py
new file mode 100644
index 00000000..2fad0f0d
--- /dev/null
+++ b/indoteknik_custom/models/promotion/promotion_product.py
@@ -0,0 +1,29 @@
+from odoo import fields, models
+
+
+class PromotionProduct(models.Model):
+ _name = "promotion.product"
+ _rec_name = "product_id"
+
+ product_id = fields.Many2one(comodel_name="product.product", string="Product variant")
+ qty = fields.Integer(string="Quantity")
+ program_line_id = fields.Many2one(comodel_name="promotion.program.line", string="Program line")
+
+ def formats(self, purchase_qty=1):
+ ir_attachment = self.env['ir.attachment']
+
+ result = []
+ for rec in self:
+ weight = rec.product_id.weight or 0
+ result.append({
+ 'id': rec.product_id.id,
+ 'image': ir_attachment.api_image('product.template', 'image_256', rec.product_id.product_tmpl_id.id),
+ 'display_name': rec.product_id.display_name,
+ 'name': rec.product_id.name,
+ 'code': rec.product_id.code,
+ 'price': rec.product_id.calculate_website_price(),
+ 'qty': rec.qty * purchase_qty,
+ 'weight': weight,
+ 'package_weight': weight * rec.qty
+ })
+ return result \ No newline at end of file
diff --git a/indoteknik_custom/models/promotion/promotion_program.py b/indoteknik_custom/models/promotion/promotion_program.py
new file mode 100644
index 00000000..90220509
--- /dev/null
+++ b/indoteknik_custom/models/promotion/promotion_program.py
@@ -0,0 +1,18 @@
+from odoo import fields, models
+
+
+class PromotionProgram(models.Model):
+ _name = "promotion.program"
+
+ name = fields.Char(string="Name")
+ banner = fields.Binary(string="Banner")
+ icon = fields.Binary(string="Icon", help="Image 1:1 ratio")
+ icon_top = fields.Binary(string="Icon Top", help="Icon ini ditampilkan sebagai atribut pada atas gambar di product card pada website")
+ icon_bottom = fields.Binary(string="Icon Bottom", help="Icon ini ditampilkan sebagai atribut pada bawah gambar di product card pada website")
+ start_time = fields.Datetime(string="Start Time")
+ end_time = fields.Datetime(string="End Time")
+ applies_to = fields.Selection(selection=[
+ ("all_user", "All User"),
+ ("login_user", "Login User")
+ ])
+ program_line = fields.One2many('promotion.program.line', 'program_id', 'Program line')
diff --git a/indoteknik_custom/models/promotion/promotion_program_line.py b/indoteknik_custom/models/promotion/promotion_program_line.py
new file mode 100644
index 00000000..a16f426d
--- /dev/null
+++ b/indoteknik_custom/models/promotion/promotion_program_line.py
@@ -0,0 +1,114 @@
+from odoo import models, fields
+from datetime import datetime
+
+
+class PromotionProgramLine(models.Model):
+ _name = 'promotion.program.line'
+
+ program_id = fields.Many2one('promotion.program', 'Program')
+ name = fields.Char('Name')
+ type = fields.Selection([
+ ("special_price", "Special Price"),
+ ("bundling", "Bundling"),
+ ("discount_loading", "Discount Loading"),
+ ("merchandise", "Merchandise")
+ ], 'Type')
+ image = fields.Binary(string="Image")
+
+ package_limit = fields.Integer('Package limit')
+ package_limit_user = fields.Integer('Package limit / user')
+ package_limit_trx = fields.Integer('Package limit / transaction')
+
+ product_ids = fields.One2many('promotion.product', 'program_line_id', 'Product')
+ free_product_ids = fields.One2many('promotion.free_product', 'program_line_id', 'Free Product')
+
+ price = fields.Float('Price')
+
+ order_promotion_ids = fields.One2many('sale.order.promotion', 'program_line_id', 'Promotions')
+
+ def get_active_promotions(self, product_id):
+ current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ return self.search([
+ ('program_id.start_time', '<=', current_time),
+ ('program_id.end_time', '>=', current_time),
+ ('product_ids.product_id', '=', product_id)
+ ])
+
+ def _res_limit_qty(self):
+ return {
+ 'all': self.package_limit,
+ 'user': self.package_limit_user,
+ 'transaction': self.package_limit_trx,
+ }
+
+ def _get_remaining_qty(self, user):
+ remaining_qty = {
+ 'all': self.package_limit,
+ 'user': self.package_limit_user,
+ 'transaction': self.package_limit_trx
+ }
+
+ for promotion in self.order_promotion_ids:
+ order = promotion.order_id
+ if order.state != 'cancel':
+ remaining_qty['all'] -= promotion.quantity
+ if user and order.partner_id.id == user['partner_id']:
+ remaining_qty['user'] -= promotion.quantity
+
+ if remaining_qty['all'] < remaining_qty['user']:
+ remaining_qty['user'] = remaining_qty['all']
+ if remaining_qty['user'] < remaining_qty['transaction']:
+ remaining_qty['transaction'] = remaining_qty['user']
+
+ return remaining_qty
+
+ def _res_type(self):
+ return {
+ 'value': self.type,
+ 'label': dict(self._fields['type'].selection).get(self.type)
+ }
+
+ def _get_remaining_time(self):
+ calculate_time = self.program_id.end_time - datetime.now()
+ return round(calculate_time.total_seconds())
+
+ def formats(self, user):
+ return [x.format(user) for x in self]
+
+ def format(self, user = None, qty = 1):
+ ir_attachment = self.env['ir.attachment']
+ limit_qty = self._res_limit_qty()
+ remaining_qty = self._get_remaining_qty(user)
+
+ percent_remaining = 0
+ if limit_qty['all'] > 0:
+ percent_remaining = (limit_qty['all'] - remaining_qty['all']) / limit_qty['all'] * 100
+
+ products = self.product_ids.formats(purchase_qty=qty)
+ free_products = self.free_product_ids.formats(purchase_qty=qty)
+
+ merged_products = products + free_products
+ weight = 0
+ if not any(x['package_weight'] == 0 for x in merged_products):
+ weight = sum(x['package_weight'] for x in merged_products)
+
+ response = {
+ 'id': self.id,
+ 'name': self.name,
+ 'image': ir_attachment.api_image(self._name, 'image', self.id),
+ 'remaining_time': self._get_remaining_time(),
+ 'type': self._res_type(),
+ 'limit_qty': limit_qty,
+ 'remaining_qty': remaining_qty,
+ 'used_percentage': percent_remaining,
+ 'price': {
+ 'price': self.price,
+ 'price_discount': self.price,
+ 'discount_percentage': 0
+ },
+ 'products': products,
+ 'free_products': free_products,
+ 'weight': weight
+ }
+
+ return response \ No newline at end of file
diff --git a/indoteknik_custom/models/promotion/sale_order.py b/indoteknik_custom/models/promotion/sale_order.py
new file mode 100644
index 00000000..abbcb64b
--- /dev/null
+++ b/indoteknik_custom/models/promotion/sale_order.py
@@ -0,0 +1,110 @@
+from odoo import fields, models
+
+
+class SaleOrder(models.Model):
+ _inherit = 'sale.order'
+
+ order_promotion_ids = fields.One2many('sale.order.promotion', 'order_id', 'Promotions')
+
+ 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['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']
+ 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):
+ 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({
+ 'company_id': 1,
+ 'order_id': self.id,
+ 'price_unit': product_price,
+ 'discount': contrib_disc_unit,
+ 'product_id': product['id'],
+ 'product_uom_qty': product['qty'],
+ 'order_promotion_id': line.id
+ })
+
+ self.env['sale.order.line'].create(order_lines)
+ self._create_promotion_program_iu_docs(iu_products)
+
+ def _merge_promotion_products(self, 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
diff --git a/indoteknik_custom/models/promotion/sale_order_line.py b/indoteknik_custom/models/promotion/sale_order_line.py
new file mode 100644
index 00000000..ba3f55f5
--- /dev/null
+++ b/indoteknik_custom/models/promotion/sale_order_line.py
@@ -0,0 +1,7 @@
+from odoo import fields, models
+
+
+class SaleOrderLine(models.Model):
+ _inherit = 'sale.order.line'
+
+ order_promotion_id = fields.Many2one('sale.order.promotion', 'Order Promotion') \ No newline at end of file
diff --git a/indoteknik_custom/models/promotion/sale_order_promotion.py b/indoteknik_custom/models/promotion/sale_order_promotion.py
new file mode 100644
index 00000000..3527f9e6
--- /dev/null
+++ b/indoteknik_custom/models/promotion/sale_order_promotion.py
@@ -0,0 +1,15 @@
+from odoo import models, fields
+
+
+class SaleOrderPromotion(models.Model):
+ _name = 'sale.order.promotion'
+
+ order_id = fields.Many2one('sale.order', 'Order')
+ order_line_ids = fields.One2many('sale.order.line', 'order_promotion_id', 'Order Line')
+ program_line_id = fields.Many2one('promotion.program.line', 'Program Line')
+ quantity = fields.Integer('Quantity')
+ is_applied = fields.Boolean('Is Applied', compute='_compute_is_applied')
+
+ def _compute_is_applied(self):
+ for rec in self:
+ rec.is_applied = len(rec.order_line_ids) > 0 # True if order line ids greater than 0