summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indoteknik_api/controllers/api_v1/cart.py21
-rw-r--r--indoteknik_api/controllers/api_v1/product_variant.py4
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py51
-rwxr-xr-xindoteknik_custom/__manifest__.py6
-rwxr-xr-xindoteknik_custom/models/__init__.py5
-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.py (renamed from indoteknik_custom/models/promotion_program.py)3
-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
-rw-r--r--indoteknik_custom/models/promotion_program_free_item.py10
-rw-r--r--indoteknik_custom/models/promotion_program_keyword.py8
-rw-r--r--indoteknik_custom/models/promotion_program_line.py171
-rwxr-xr-xindoteknik_custom/models/sale_order.py52
-rw-r--r--indoteknik_custom/models/sale_order_line.py1
-rw-r--r--indoteknik_custom/models/website_user_cart.py41
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv5
-rw-r--r--indoteknik_custom/views/promotion/promotion_program.xml (renamed from indoteknik_custom/views/promotion_program.xml)1
-rw-r--r--indoteknik_custom/views/promotion/promotion_program_free_item.xml (renamed from indoteknik_custom/views/promotion_program_free_item.xml)0
-rw-r--r--indoteknik_custom/views/promotion/promotion_program_keyword.xml (renamed from indoteknik_custom/views/promotion_program_keyword.xml)0
-rw-r--r--indoteknik_custom/views/promotion/promotion_program_line.xml72
-rw-r--r--indoteknik_custom/views/promotion_program_line.xml98
-rwxr-xr-xindoteknik_custom/views/sale_order.xml17
-rwxr-xr-xindoteknik_custom/views/website_user_cart.xml12
27 files changed, 476 insertions, 413 deletions
diff --git a/indoteknik_api/controllers/api_v1/cart.py b/indoteknik_api/controllers/api_v1/cart.py
index 6faac27f..2243ec0f 100644
--- a/indoteknik_api/controllers/api_v1/cart.py
+++ b/indoteknik_api/controllers/api_v1/cart.py
@@ -31,7 +31,7 @@ class Cart(controller.Controller):
return self.response(carts)
@http.route(PREFIX_USER + 'cart/create-or-update', auth='public', methods=['POST', 'OPTIONS'], csrf=False)
- @controller.Controller.must_authorized()
+ @controller.Controller.must_authorized(private=True, private_key='user_id')
def create_or_update_cart(self, user_id, **kw):
# Convert input values to appropriate types
user_id = int(user_id)
@@ -54,19 +54,29 @@ class Cart(controller.Controller):
user_query = ('user_id', '=', user_id)
website_user_cart.search([user_query, ('source', '=', 'buy')]).unlink()
- # Prepare query to find existing cart entry for the product
- query = [user_query, ('product_id', '=', product_id), ('source', '=', 'add_to_cart')]
+ # Prepare query to find existing cart entry
+ query = [user_query, ('source', '=', 'add_to_cart')]
+ if product_id:
+ query.append(('product_id', '=', product_id))
+ elif program_line_id:
+ query.append(('program_line_id', '=', program_line_id))
+
cart = website_user_cart.search(query, limit=1)
- result = {}
+
data_to_update = {
'qty': qty,
'is_selected': is_selected,
- 'program_line_id': program_line_id
+ 'program_line_id': program_line_id,
+ 'product_id': product_id
}
+ if program_line_id:
+ data_to_update['product_id'] = False
+
if source:
data_to_update['source'] = source
+ result = {}
if cart and source in (None, 'add_to_cart'):
# Update existing cart entry
cart.write(data_to_update)
@@ -75,7 +85,6 @@ class Cart(controller.Controller):
# Create a new cart entry if it doesn't exist
create = website_user_cart.create({
'user_id': user_id,
- 'product_id': product_id,
**data_to_update
})
result['id'] = create.id
diff --git a/indoteknik_api/controllers/api_v1/product_variant.py b/indoteknik_api/controllers/api_v1/product_variant.py
index 8de4669e..fce2edf0 100644
--- a/indoteknik_api/controllers/api_v1/product_variant.py
+++ b/indoteknik_api/controllers/api_v1/product_variant.py
@@ -21,7 +21,7 @@ class ProductVariant(controller.Controller):
return self.response(data)
- @http.route(prefix + 'product_variant/<product_id>/promotions', auth='public', methods=['GET', 'OPTIONS'])
+ @http.route(prefix + 'product-variant/<product_id>/promotions', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_product_variant_promotions(self, product_id):
product_id = int(product_id)
@@ -29,6 +29,6 @@ class ProductVariant(controller.Controller):
program_line = request.env['promotion.program.line']
program_lines = program_line.get_active_promotions(product_id)
- program_lines = program_lines.res_format(user=user_data)
+ program_lines = program_lines.formats(user=user_data)
return self.response(program_lines)
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 2c6958d8..ef4c2688 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -295,47 +295,44 @@ class SaleOrder(controller.Controller):
if params['value']['type'] == 'sale_order':
parameters['approval_status'] = 'pengajuan1'
sale_order = request.env['sale.order'].create([parameters])
- parameters = []
user_id = params['value']['user_id']
user_cart = request.env['website.user.cart']
source = params['value']['source']
- products = user_cart.get_product_by_user(user_id=user_id, selected=True, source=source)
- for product in products:
- price_unit = product['price']['price']
- price_discount =product['price']['discount_percentage']
- order_line = {
- 'company_id': 1,
- 'order_id': sale_order.id,
- 'price_unit': price_unit,
- 'discount': price_discount,
- 'product_id': product['id'],
- 'product_uom_qty': product['quantity']
- }
-
- if product['program']:
- order_line.update({
- 'program_line_id': product['program']['id']
+ carts = user_cart.get_product_by_user(user_id=user_id, selected=True, source=source)
+
+ order_lines = []
+ promotions = []
+ for cart in carts:
+ if cart['cart_type'] == 'product':
+ order_lines.append({
+ 'company_id': 1,
+ 'order_id': sale_order.id,
+ 'price_unit': cart['price']['price'],
+ 'discount': cart['price']['discount_percentage'],
+ 'product_id': cart['id'],
+ 'product_uom_qty': cart['quantity']
+ })
+ elif cart['cart_type'] == 'promotion':
+ promotions.append({
+ 'order_id': sale_order.id,
+ 'program_line_id': cart['id'],
+ 'quantity': cart['quantity']
})
- parameters.append(order_line)
- # if product['program'] and product['program']['type']['value'] != 'special_price':
- # total_qty += sum(x['quantity'] for x in product['program']['items'])
- # price_unit = product['subtotal'] / total_qty
- # price_discount = 0
-
- request.env['sale.order.line'].create(parameters)
+ request.env['sale.order.line'].create(order_lines)
+ request.env['sale.order.promotion'].create(promotions)
- if any(x['program'] for x in products):
+ if len(promotions) > 0:
sale_order.apply_promotion_program()
voucher_code = params['value']['voucher']
voucher = request.env['voucher'].search([('code', '=', voucher_code)])
- if voucher:
+ if voucher and len(promotions) == 0:
sale_order.voucher_id = voucher.id
sale_order.apply_voucher()
- cart_ids = [x['cart_id'] for x in products]
+ cart_ids = [x['cart_id'] for x in carts]
# user_cart.browse(cart_ids).unlink()
return self.response({
'id': sale_order.id,
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 55708acc..10878e32 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -75,10 +75,8 @@
'views/procurement_monitoring_detail.xml',
'views/product_product.xml',
'views/brand_vendor.xml',
- 'views/promotion_program.xml',
- 'views/promotion_program_line.xml',
- 'views/promotion_program_free_item.xml',
- 'views/promotion_program_keyword.xml',
+ 'views/promotion/promotion_program.xml',
+ 'views/promotion/promotion_program_line.xml',
'views/requisition.xml',
'views/landedcost.xml',
'views/product_sla.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 77779d22..f51f95af 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -15,10 +15,6 @@ from . import product_pricelist
from . import product_public_category
from . import product_spec
from . import product_template
-from . import promotion_program
-from . import promotion_program_line
-from . import promotion_program_free_item
-from . import promotion_program_keyword
from . import purchase_order_line
from . import purchase_order
from . import purchase_outstanding
@@ -90,3 +86,4 @@ from . import cost_centre
from . import account_account
from . import account_move_line
from . import stock_scheduler_compute
+from . import promotion \ No newline at end of file
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_program.py b/indoteknik_custom/models/promotion/promotion_program.py
index e60f48e1..90220509 100644
--- a/indoteknik_custom/models/promotion_program.py
+++ b/indoteknik_custom/models/promotion/promotion_program.py
@@ -15,5 +15,4 @@ class PromotionProgram(models.Model):
("all_user", "All User"),
("login_user", "Login User")
])
- program_line = fields.One2many(comodel_name="promotion.program.line", inverse_name="program_id", string="Program Line")
- keywords = fields.One2many(comodel_name="promotion.program.keyword", inverse_name="program_id", string="Keywords")
+ 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
diff --git a/indoteknik_custom/models/promotion_program_free_item.py b/indoteknik_custom/models/promotion_program_free_item.py
deleted file mode 100644
index 705456dd..00000000
--- a/indoteknik_custom/models/promotion_program_free_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from odoo import fields, models
-
-
-class PromotionProgramFreeItem(models.Model):
- _name = "promotion.program.free_item"
- _rec_name = "product_id"
-
- product_id = fields.Many2one(comodel_name="product.product", string="Product Variant")
- qty = fields.Integer(string="Qty")
- line_id = fields.Many2one(comodel_name="promotion.program.line", string="Program Line")
diff --git a/indoteknik_custom/models/promotion_program_keyword.py b/indoteknik_custom/models/promotion_program_keyword.py
deleted file mode 100644
index 79b938e2..00000000
--- a/indoteknik_custom/models/promotion_program_keyword.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from odoo import fields, models
-
-
-class PromotionProgramKeyword(models.Model):
- _name = "promotion.program.keyword"
-
- name = fields.Char(string="Keyword")
- program_id = fields.Many2one(comodel_name="promotion.program")
diff --git a/indoteknik_custom/models/promotion_program_line.py b/indoteknik_custom/models/promotion_program_line.py
deleted file mode 100644
index 71418d6c..00000000
--- a/indoteknik_custom/models/promotion_program_line.py
+++ /dev/null
@@ -1,171 +0,0 @@
-from odoo import fields, models, api
-from datetime import datetime
-import math
-
-
-class PromotionProgramLine(models.Model):
- _name = "promotion.program.line"
-
- name = fields.Char(string="Name")
- image = fields.Binary(string="Image")
- product_id = fields.Many2one(comodel_name="product.product", string="Product Variant")
- program_id = fields.Many2one(comodel_name="promotion.program", string="Program")
- discount_type = fields.Selection(selection=[
- ("percentage", "Percentage"),
- ("fixed_price", "Fixed Price"),
- ], string="Discount Type")
- discount_amount = fields.Float(string="Discount Amount")
- promotion_type = fields.Selection(string="Promotion Type",
- selection=[
- ("special_price", "Special Price"),
- ("bundling", "Bundling"),
- ("discount_loading", "Discount Loading"),
- ("merchandise", "Merchandise")
- ],
- help='- Special Price: Potongan harga barang.\n'
- '- Bundling: Menggabungkan beberapa produk menjadi satu paket.\n'
- '- Discount Loading: Semakin banyak unit yang dibeli maka semakin murah harga yang dibayar.\n'
- '- Merchandise: Pemberian barang gratis.'
- )
- minimum_purchase_qty = fields.Integer(string="Minimum Purchase Qty", help="Minimum unit barang yang perlu dibeli untuk dapat menggunakan promosi")
- applies_multiply = fields.Boolean(string="Applies Multiply", help="Diterapkan berlipat ganda")
- limit_qty = fields.Integer(string="Limit Qty", help="Kuota promosi keseluruhan")
- limit_qty_user = fields.Integer(string="Limit Qty / User", help="Limit Qty per User")
- limit_qty_transaction = fields.Integer(string="Limit Qty / Transaction", help="Limit Qty per Transaction")
- line_free_item = fields.One2many(comodel_name="promotion.program.free_item", inverse_name="line_id", string="Line Free Item")
- display_on_homepage = fields.Boolean(string="Display on Homepage")
- order_line_ids = fields.One2many('sale.order.line', 'program_line_id')
-
- @api.onchange('product_id')
- def _onchange_product_id(self):
- if self.product_id and not self.name:
- self.name = self.product_id.display_name
- self._discount_loading_auto()
-
- @api.onchange('promotion_type')
- def onchange_promotion_type(self):
- self._discount_loading_auto()
-
- def _discount_loading_auto(self):
- program_line = self
- product = program_line.product_id
- promotion_type = program_line.promotion_type
-
- if promotion_type != 'discount_loading' or not product:
- return
-
- line = self.browse(self.ids)
- line.product_id = self.product_id.id
- line.promotion_type = self.promotion_type
-
- product_added = False
- line_free_item = program_line.line_free_item
- for line in line_free_item:
- if line.product_id.id == product.id:
- product_added = True
- continue
- line.unlink()
- if not product_added:
- data = {'product_id': product.id,
- 'qty': 1, 'line_id': program_line.id}
- line_free_item.create(data)
-
- def calculate_price(self, price):
- initial_price = price['price']
- if self.discount_type == 'percentage':
- price['discount_percentage'] = self.discount_amount
- price['price_discount'] = initial_price - (initial_price * self.discount_amount / 100)
- elif self.discount_type == 'fixed_price':
- price['price_discount'] = self.discount_amount
- price['discount_percentage'] = round((initial_price - self.discount_amount) / initial_price * 100)
- return price
-
- 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_id', '=', product_id)
- ])
-
- def _get_remaining_qty(self, user):
- remaining_qty = {
- 'all': self.limit_qty,
- 'user': self.limit_qty_user,
- 'transaction': self.limit_qty_transaction
- }
- for order in self.order_line_ids:
- parent_order = order.order_id
- if parent_order.state != 'cancel':
- remaining_qty['all'] -= order.product_uom_qty
- if user and parent_order.partner_id.id == user['partner_id']:
- remaining_qty['user'] -= order.product_uom_qty
-
- 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 _get_remaining_time(self):
- calculate_time = self.program_id.end_time - datetime.now()
- return round(calculate_time.total_seconds())
-
- def _res_limit_qty(self):
- return {
- 'all': self.limit_qty,
- 'user': self.limit_qty_user,
- 'transaction': self.limit_qty_transaction,
- }
-
- def _res_promotion_type(self):
- return {
- 'value': self.promotion_type,
- 'label': dict(self._fields['promotion_type'].selection).get(self.promotion_type)
- }
-
- def format(self, user = None, qty = 0):
- ir_attachment = self.env['ir.attachment']
- product_price = self.product_id.calculate_website_price()
- 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
-
- qty_can_buy = min(remaining_qty['user'], qty)
- if self.limit_qty_transaction > 0:
- qty_can_buy = min(qty_can_buy, self.limit_qty_transaction)
-
- multiplier = 0
- if qty_can_buy > self.minimum_purchase_qty:
- multiplier = 1
-
- if self.applies_multiply and qty_can_buy > 0:
- multiplier = math.floor(qty_can_buy / self.minimum_purchase_qty)
-
- response = {
- 'id': self.id,
- 'name': self.name,
- 'image': ir_attachment.api_image('promotion.program.line', 'image', self.id),
- 'minimum_purchase_qty': self.minimum_purchase_qty,
- 'applies_multiply': self.applies_multiply,
- 'remaining_time': self._get_remaining_time(),
- 'type': self._res_promotion_type(),
- 'limit_qty': limit_qty,
- 'remaining_qty': remaining_qty,
- 'used_percentage': percent_remaining,
- 'price': self.calculate_price(price=product_price),
- 'items': [{
- 'product_id': line.product_id.id,
- 'quantity': line.qty * multiplier
- } for line in self.line_free_item]
- }
-
- return response
-
- def res_format(self, user):
- data = [x.format(user) for x in self]
- return data
-
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 2510d9e5..376b8e40 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -448,58 +448,6 @@ class SaleOrder(models.Model):
line.discount = line.initial_discount
line.initial_discount = False
- def apply_promotion_program(self):
- userdata = {
- 'user_id': self.partner_id.user_id.id,
- 'partner_id': self.partner_id.id
- }
-
- iu_items = []
- for line in self.order_line:
- promotion = line.program_line_id.format(user=userdata, qty=line.product_uom_qty)
- if promotion['type']['value'] == 'merchandise':
- iu_items += filter(lambda x: x['quantity'] > 0, promotion['items'])
-
- if len(iu_items) > 0:
- self._create_promotion_program_iu_docs(iu_items)
-
- def _create_promotion_program_iu_docs(self, items):
- 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
- })
-
- product_model = self.env['product.product']
- for item in items:
- picking.move_ids_without_package.create({
- 'product_id': item['product_id'],
- 'name': product_model.browse(item['product_id']).display_name,
- 'product_uom_qty': item['quantity'],
- '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/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index 4ec48b55..69328325 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -21,7 +21,6 @@ class SaleOrderLine(models.Model):
note_procurement = fields.Char(string='Note', help="Harap diisi jika ada keterangan tambahan dari Procurement, agar dapat dimonitoring")
vendor_subtotal = fields.Float(string='Vendor Subtotal', compute="_compute_vendor_subtotal")
amount_voucher_disc = fields.Float(string='Voucher Discount')
- program_line_id = fields.Many2one('promotion.program.line', 'Program Line')
def _compute_vendor_subtotal(self):
for line in self:
diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py
index 1468e9dc..47a695fe 100644
--- a/indoteknik_custom/models/website_user_cart.py
+++ b/indoteknik_custom/models/website_user_cart.py
@@ -22,28 +22,33 @@ class WebsiteUserCart(models.Model):
record.user_other_carts = others
def get_product(self):
- product = self.product_id.v2_api_single_response(self.product_id)
- product['cart_id'] = self.id
- product['quantity'] = self.qty
- product['subtotal'] = self.qty * product['price']['price_discount']
- product['selected'] = self.is_selected
- product['program'] = None
- product['can_buy'] = True
- flashsales = self.product_id._get_active_flash_sale()
- product['has_flashsale'] = True if len(flashsales) > 0 else False
- if self.program_line_id:
+ res = {
+ 'cart_id': self.id,
+ 'quantity': self.qty,
+ 'selected': self.is_selected,
+ 'can_buy': True
+ }
+
+ if self.product_id:
+ res['cart_type'] = 'product'
+ product = self.product_id.v2_api_single_response(self.product_id)
+ res.update(product)
+ flashsales = self.product_id._get_active_flash_sale()
+ res['has_flashsale'] = True if len(flashsales) > 0 else False
+ elif self.program_line_id:
+ res['cart_type'] = 'promotion'
userdata = {
'partner_id': self.user_id.partner_id.id,
'user_id': self.user_id.id
}
- product['program'] = self.program_line_id.format(user=userdata, qty=self.qty)
-
- if product['program']:
- if self.qty < product['program']['minimum_purchase_qty'] or self.qty > product['program']['remaining_qty']['transaction']:
- product['can_buy'] = False
- product['price'] = product['program']['price']
-
- return product
+ program = self.program_line_id.format(user=userdata, qty=self.qty)
+ res.update(program)
+ if program['remaining_qty']['transaction'] < self.qty:
+ res['can_buy'] = False
+
+ res['subtotal'] = self.qty * res['price']['price_discount']
+
+ return res
def get_products(self):
return [x.get_product() for x in self]
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 5e77f25d..21bbe093 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -53,8 +53,8 @@ access_rajaongkir_kurir,access.rajaongkir.kurir,model_rajaongkir_kurir,,1,1,1,1
access_brand_vendor,access.brand.vendor,model_brand_vendor,,1,1,1,1
access_promotion_program,access.promotion.program,model_promotion_program,,1,1,1,1
access_promotion_program_line,access.promotion.program.line,model_promotion_program_line,,1,1,1,1
-access_promotion_program_free_item,access.promotion.program.free_item,model_promotion_program_free_item,,1,1,1,1
-access_promotion_program_keyword,access.promotion.program.keyword,model_promotion_program_keyword,,1,1,1,1
+access_promotion_product,access.promotion.product,model_promotion_product,,1,1,1,1
+access_promotion_free_product,access.promotion.free_product,model_promotion_free_product,,1,1,1,1
access_requisition,access.requisition,model_requisition,,1,1,1,1
access_requisition_line,access.requisition.line,model_requisition_line,,1,1,1,1
access_requisition_purchase_match,access.requisition.purchase.match,model_requisition_purchase_match,,1,1,1,1
@@ -74,3 +74,4 @@ access_mrp_production,access.mrp.production,model_mrp_production,,1,1,1,1
access_apache_solr_queue,access.apache.solr.queue,model_apache_solr_queue,,1,1,1,1
access_cost_centre,access.cost.centre,model_cost_centre,,1,1,1,1
access_stock_scheduler_compute,access.stock.scheduler.compute,model_stock_scheduler_compute,,1,1,1,1
+access_sale_order_promotion,access.sale.order.promotion,model_sale_order_promotion,,1,1,1,1
diff --git a/indoteknik_custom/views/promotion_program.xml b/indoteknik_custom/views/promotion/promotion_program.xml
index 5dff80e6..99fbd878 100644
--- a/indoteknik_custom/views/promotion_program.xml
+++ b/indoteknik_custom/views/promotion/promotion_program.xml
@@ -21,7 +21,6 @@
<group>
<group>
<field name="name" />
- <field name="keywords" widget="many2many_tags" />
<field name="banner" widget="image" height="160" />
</group>
<group>
diff --git a/indoteknik_custom/views/promotion_program_free_item.xml b/indoteknik_custom/views/promotion/promotion_program_free_item.xml
index 52d421fe..52d421fe 100644
--- a/indoteknik_custom/views/promotion_program_free_item.xml
+++ b/indoteknik_custom/views/promotion/promotion_program_free_item.xml
diff --git a/indoteknik_custom/views/promotion_program_keyword.xml b/indoteknik_custom/views/promotion/promotion_program_keyword.xml
index fd279665..fd279665 100644
--- a/indoteknik_custom/views/promotion_program_keyword.xml
+++ b/indoteknik_custom/views/promotion/promotion_program_keyword.xml
diff --git a/indoteknik_custom/views/promotion/promotion_program_line.xml b/indoteknik_custom/views/promotion/promotion_program_line.xml
new file mode 100644
index 00000000..6fdf0a9a
--- /dev/null
+++ b/indoteknik_custom/views/promotion/promotion_program_line.xml
@@ -0,0 +1,72 @@
+<odoo>
+ <record id="promotion_program_line_tree" model="ir.ui.view">
+ <field name="name">Promotion Program Line Tree</field>
+ <field name="model">promotion.program.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="name" />
+ <field name="type" />
+ <field name="product_ids" />
+ <field name="free_product_ids" />
+ <field name="price" />
+ </tree>
+ </field>
+ </record>
+
+ <record id="promotion_program_line_form" model="ir.ui.view">
+ <field name="name">Promotion Program Line Form</field>
+ <field name="model">promotion.program.line</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <group>
+ <field name="name" />
+ <field name="type" />
+ <field name="image" widget="image" height="160" />
+ </group>
+ <group>
+ <field name="package_limit" />
+ <field name="package_limit_user" />
+ <field name="package_limit_trx" />
+ <field name="price" />
+ </group>
+ </group>
+ <notebook>
+ <page string="Product Required" name="product_required">
+ <field name="product_ids">
+ <tree editable="top">
+ <field name="product_id" />
+ <field name="qty" />
+ </tree>
+ </field>
+ </page>
+ <page string="Free Product" name="free_product">
+ <field name="free_product_ids">
+ <tree editable="top">
+ <field name="product_id" />
+ <field name="qty" />
+ </tree>
+ </field>
+ </page>
+ </notebook>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="promotion_program_line_action" model="ir.actions.act_window">
+ <field name="name">Promotion Program Line</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">promotion.program.line</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_promotion_program_line"
+ name="Program Line"
+ parent="indoteknik_custom.menu_promotion_program_parent"
+ sequence="2"
+ action="promotion_program_line_action"
+ />
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/promotion_program_line.xml b/indoteknik_custom/views/promotion_program_line.xml
deleted file mode 100644
index 5963d0dd..00000000
--- a/indoteknik_custom/views/promotion_program_line.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<odoo>
- <record id="promotion_program_line_tree" model="ir.ui.view">
- <field name="name">Promotion Program Line Tree</field>
- <field name="model">promotion.program.line</field>
- <field name="arch" type="xml">
- <tree>
- <field name="name" />
- <field name="promotion_type" />
- <field name="limit_qty" />
- <field name="limit_qty_user" />
- <field name="limit_qty_transaction" />
- <field name="line_free_item" />
- <field name="display_on_homepage" />
- </tree>
- </field>
- </record>
-
- <record id="promotion_program_line_form" model="ir.ui.view">
- <field name="name">Promotion Program Line Form</field>
- <field name="model">promotion.program.line</field>
- <field name="arch" type="xml">
- <form>
- <sheet>
- <group>
- <group>
- <field name="name" />
- <field name="promotion_type"/>
- <field name="product_id" />
- <field name="image" widget="image" height="160" />
- </group>
- <group>
- <field name="limit_qty" />
- <field name="limit_qty_user" />
- <field name="limit_qty_transaction" />
- <field
- name="discount_type"
- attrs="{'invisible': [('promotion_type', '!=', 'special_price')]}"
- />
- <field
- name="discount_amount"
- attrs="{'invisible': [('promotion_type', '!=', 'special_price')]}"
- />
- <field
- name="minimum_purchase_qty"
- attrs="{'invisible': [('promotion_type', 'not in', ['discount_loading', 'bundling', 'merchandise'])]}"
- />
- <field
- name="applies_multiply"
- attrs="{'invisible': [('promotion_type', 'not in', ['discount_loading', 'bundling', 'merchandise'])]}"
- />
- <field
- name="display_on_homepage"
- attrs="{'invisible': [('promotion_type', '!=', 'special_price')]}"
- />
- </group>
- </group>
- <notebook>
- <page
- string="Line Free Item"
- name="line_free_items"
- attrs="{'invisible': [('promotion_type', '=', 'special_price')]}"
- >
- <field name="line_free_item" />
- </page>
- <page
- string="Order Line"
- name="order_line"
- >
- <field name="order_line_ids" readonly="1">
- <tree>
- <field name="order_id" />
- <field name="name" />
- <field name="product_id" />
- <field name="product_uom_qty" />
- </tree>
- </field>
- </page>
- </notebook>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="promotion_program_line_action" model="ir.actions.act_window">
- <field name="name">Promotion Program Line</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">promotion.program.line</field>
- <field name="view_mode">tree,form</field>
- </record>
-
- <menuitem
- id="menu_promotion_program_line"
- name="Program Line"
- parent="indoteknik_custom.menu_promotion_program_parent"
- sequence="2"
- action="promotion_program_line_action"
- />
-</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 3ea65498..282e4024 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -91,7 +91,6 @@
<field name="note_procurement" optional="hide"/>
<field name="vendor_subtotal" optional="hide"/>
<field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide"/>
- <field name="program_line_id" optional="1" />
</xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" position="before">
<field name="line_no" readonly="1" optional="hide"/>
@@ -132,6 +131,22 @@
</group>
</group>
</page>
+ <page string="Promotion" name="page_promotion">
+ <field name="order_promotion_ids" readonly="1">
+ <tree options="{'no_open': True}">
+ <field name="program_line_id" />
+ <field name="quantity" />
+ <field name="is_applied" />
+ </tree>
+ <form>
+ <group>
+ <field name="program_line_id" />
+ <field name="quantity" />
+ <field name="is_applied" />
+ </group>
+ </form>
+ </field>
+ </page>
</page>
</field>
diff --git a/indoteknik_custom/views/website_user_cart.xml b/indoteknik_custom/views/website_user_cart.xml
index f43c9f63..e3630363 100755
--- a/indoteknik_custom/views/website_user_cart.xml
+++ b/indoteknik_custom/views/website_user_cart.xml
@@ -29,12 +29,12 @@
<sheet>
<group>
<group>
- <field name="user_id"/>
- <field name="product_id"/>
- <field name="program_line_id" domain="[('product_id', '=', product_id)]"/>
- <field name="qty"/>
- <field name="is_selected"/>
- <field name="source"/>
+ <field name="user_id" />
+ <field name="product_id" />
+ <field name="program_line_id" />
+ <field name="qty" />
+ <field name="is_selected" />
+ <field name="source" />
</group>
<group></group>
</group>