summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2022-10-05 15:19:27 +0700
committerIT Fixcomart <it@fixcomart.co.id>2022-10-05 15:19:27 +0700
commit14cb30c3f2fb8b15baaf32ab9fca7bb9bda73845 (patch)
treee4039e13e934db6ee148f766882bb82af8db1d7a
parentd1bc570eae2818bc4b535840f2eb3061b99ca98b (diff)
Update struktur API and fitur API flash sale
-rw-r--r--indoteknik_api/__init__.py2
-rw-r--r--indoteknik_api/__manifest__.py19
-rw-r--r--indoteknik_api/controllers/__init__.py3
-rw-r--r--indoteknik_api/controllers/controller.py (renamed from indoteknik_custom/controllers/api_controller.py)9
-rw-r--r--indoteknik_api/controllers/product_controller.py142
-rw-r--r--indoteknik_api/controllers/sale_order_controller.py (renamed from indoteknik_custom/controllers/api/sale_order.py)4
-rw-r--r--indoteknik_api/models/__init__.py2
-rw-r--r--indoteknik_api/models/product_pricelist.py108
-rw-r--r--indoteknik_api/models/product_template.py0
-rwxr-xr-xindoteknik_custom/__init__.py1
-rwxr-xr-xindoteknik_custom/__manifest__.py2
-rw-r--r--indoteknik_custom/controllers/__init__.py2
-rw-r--r--indoteknik_custom/controllers/api/__init__.py2
-rw-r--r--indoteknik_custom/controllers/api/product.py95
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/product_pricelist.py12
-rw-r--r--indoteknik_custom/views/product_pricelist.xml28
-rwxr-xr-xindoteknik_custom/views/product_pricelist_item.xml37
18 files changed, 361 insertions, 108 deletions
diff --git a/indoteknik_api/__init__.py b/indoteknik_api/__init__.py
new file mode 100644
index 00000000..91c5580f
--- /dev/null
+++ b/indoteknik_api/__init__.py
@@ -0,0 +1,2 @@
+from . import controllers
+from . import models
diff --git a/indoteknik_api/__manifest__.py b/indoteknik_api/__manifest__.py
new file mode 100644
index 00000000..b1ebd9a1
--- /dev/null
+++ b/indoteknik_api/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'API Indoteknik',
+ 'version': '1.0',
+ 'category': '',
+ 'sequence': 1,
+ 'summary': 'API Indoteknik',
+ 'description': '',
+ 'author': 'PT. Indoteknik Dotcom Gemilang',
+ 'website': '',
+ 'images': ['assets/favicon.ico'],
+ 'depends': ['base', 'sale'],
+ 'data': [],
+ 'demo': [],
+ 'css': [],
+ 'installable': True,
+ 'application': False,
+ 'auto_install': False,
+ 'license': '',
+}
diff --git a/indoteknik_api/controllers/__init__.py b/indoteknik_api/controllers/__init__.py
new file mode 100644
index 00000000..4c317fdc
--- /dev/null
+++ b/indoteknik_api/controllers/__init__.py
@@ -0,0 +1,3 @@
+from . import controller
+from . import product_controller
+from . import sale_order_controller
diff --git a/indoteknik_custom/controllers/api_controller.py b/indoteknik_api/controllers/controller.py
index faf8b640..fb5f1fce 100644
--- a/indoteknik_custom/controllers/api_controller.py
+++ b/indoteknik_api/controllers/controller.py
@@ -1,13 +1,13 @@
import datetime
import base64
+import json
from odoo import http
from odoo.http import request
-import json
from pytz import timezone
-class ApiController(http.Controller):
+class Controller(http.Controller):
def authenticate(self, kw):
db = kw.get('db')
username = kw.get('username')
@@ -34,13 +34,10 @@ class ApiController(http.Controller):
return request.make_response(response, [('Content-Type', 'application/json')])
def search_with_api_params(self, model: str, kw, domain=[]):
+ """ To search data by default API Params if exist """
limit = kw.get('limit', 0)
offset = kw.get('offset', 0)
order = kw.get('order', '')
- # domain = kw.get('domain', [])
- # if domain:
- # domain = json.loads(domain)
- # domain += default_domain
return request.env[model].search(domain, limit=int(limit), offset=int(offset), order=order)
diff --git a/indoteknik_api/controllers/product_controller.py b/indoteknik_api/controllers/product_controller.py
new file mode 100644
index 00000000..a127b790
--- /dev/null
+++ b/indoteknik_api/controllers/product_controller.py
@@ -0,0 +1,142 @@
+from datetime import datetime
+from . import controller
+from odoo import http
+from odoo.http import request
+
+
+class ProductController(controller.Controller):
+ prefix_url = '/api/product'
+
+ # TODO: separate function for get manufacture and promotion by product_id
+ @http.route(prefix_url + '/search', auth='public', methods=['GET'])
+ def search_product(self, **kw):
+ self.authenticate(kw)
+ base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
+
+ query = kw.get('query')
+ if not query:
+ return self.response(code=400, description='Field query is required')
+
+ query = '%' + query.replace(' ', '%') + '%'
+ domain = [
+ ('sale_ok', '=', True),
+ '|',
+ ('default_code', 'ilike', query),
+ ('name', 'ilike', query)
+ ]
+
+ manufactures = kw.get('manufactures')
+ if manufactures:
+ manufactures = [int(x) for x in manufactures.split(',')]
+ domain.append(('x_manufacture', 'in', manufactures))
+
+ categories = kw.get('categories')
+ if categories:
+ categories = [int(x) for x in categories.split(',')]
+ domain.append(('public_categ_ids', 'child_of', categories))
+
+ product_variants = request.env['product.product'].search(domain)
+ product_variant_ids = [x.id for x in product_variants]
+
+ domain = [('product_variant_ids', 'in', product_variant_ids)]
+ product_templates = self.search_with_api_params('product.template', kw, domain)
+ data = {
+ 'total_records': len(request.env['product.template'].search(domain)),
+ 'products': []
+ }
+ for product_template in product_templates:
+ discount_price = 0
+ price = product_template.web_price
+ if price > 0:
+ if product_template.taxes_id:
+ if not product_template.taxes_id.price_include:
+ price += (price * product_template.taxes_id.amount / 100)
+ else:
+ price += (price * 11 / 100)
+
+ promotion = self.get_promotion_by_product(product_template)
+ if len(promotion) > 0:
+ discount_price = price - (price * promotion['discount_percentage'] / 100)
+
+ manufacture = self.get_manufacture_by_product(product_template)
+ stock = self.get_stock_by_product(product_template)
+
+ # TODO: remove price and discount_price if old indoteknik not use
+ data['products'].append({
+ 'id': product_template.id,
+ 'image': base_url + 'api/image/product.template/image_128/' + str(product_template.id) if product_template.image_128 else '',
+ 'name': product_template.name,
+ # 'lowest_price': request.env['product.pricelist'].get_lowest_product_variant_price(product_template.id, 4),
+ 'price': price,
+ 'discount_price': discount_price,
+ 'total_variant': len(product_template.product_variant_ids),
+ 'stock': stock,
+ 'manufacture': manufacture,
+ 'promotion': promotion,
+ })
+ return self.response(data)
+
+ @http.route(prefix_url + '/flash_sale', auth='public', methods=['GET'])
+ def get_flash_sale_product(self, **kw):
+ self.authenticate(kw)
+ base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ product_pricelist_default = request.env['ir.config_parameter'].sudo().get_param('product.pricelist.default')
+ active_flash_sale = request.env['product.pricelist'].get_active_flash_sale()
+ data = {}
+ if active_flash_sale:
+ flash_sale = {
+ 'banner': base_url + 'api/image/product.pricelist/banner/' + str(active_flash_sale.id) if active_flash_sale.banner else '',
+ 'duration': (active_flash_sale.end_date - datetime.now()).total_seconds(),
+ 'products': []
+ }
+ product_pricelist_item = request.env['product.pricelist.item'].search([('pricelist_id', '=', active_flash_sale.id)])
+ product_variant_ids = [x.product_id.id for x in product_pricelist_item]
+ product_templates = self.search_with_api_params('product.template', kw, [('product_variant_ids', 'in', product_variant_ids)])
+ for product in product_templates:
+ flash_sale['products'].append({
+ 'id': product.id,
+ 'name': product.name,
+ 'image': base_url + 'api/image/product.template/image_128/' + str(product.id) if product.image_128 else '',
+ 'lowest_price': request.env['product.pricelist'].get_lowest_product_variant_price(product.id, int(product_pricelist_default)),
+ 'stock': self.get_stock_by_product(product),
+ 'total_variant': len(product.product_variant_ids),
+ 'manufacture': self.get_manufacture_by_product(product),
+ 'promotion': self.get_promotion_by_product(product),
+ })
+ data.update({'flash_sale': flash_sale})
+ return self.response(data)
+
+ def get_stock_by_product(self, product_template: object):
+ stock = 0
+ for product_variant in product_template.product_variant_ids:
+ stock += product_variant.qty_stock_vendor
+ return stock
+
+ def get_manufacture_by_product(self, product_template: object):
+ manufacture = {}
+ if product_template.x_manufacture:
+ manufacture.update({
+ 'id': product_template.x_manufacture.id,
+ 'name': product_template.x_manufacture.x_name,
+ })
+ return manufacture
+
+ def get_promotion_by_product(self, product_template: object):
+ base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ promotion = {}
+ if product_template.x_manufacture:
+ domain = [
+ ('rule_products_domain', 'ilike', product_template.x_manufacture.x_name),
+ ('active', '=', True)
+ ]
+ coupon_program = request.env['coupon.program'].search(domain, limit=1)
+ if coupon_program:
+ icon_1 = (base_url + 'api/image/coupon.program/x_studio_field_Ifopn/' + str(coupon_program.id)) if coupon_program.x_studio_field_Ifopn else ''
+ icon_2 = (base_url + 'api/image/coupon.program/x_studio_field_2Ul77/' + str(coupon_program.id)) if coupon_program.x_studio_field_2Ul77 else ''
+ promotion.update({
+ 'name': coupon_program.name,
+ 'discount_percentage': coupon_program.discount_percentage,
+ 'icon_1': icon_1,
+ 'icon_2': icon_2
+ })
+ return promotion \ No newline at end of file
diff --git a/indoteknik_custom/controllers/api/sale_order.py b/indoteknik_api/controllers/sale_order_controller.py
index c00a94d3..741d4bf8 100644
--- a/indoteknik_custom/controllers/api/sale_order.py
+++ b/indoteknik_api/controllers/sale_order_controller.py
@@ -1,9 +1,9 @@
-from .. import api_controller
+from . import controller
from odoo import http
from odoo.http import request
-class SaleOrderApi(api_controller.ApiController):
+class SaleOrderController(controller.Controller):
@http.route('/api/sale_order/invoiced', auth='public', methods=['GET'])
def get_sale_order_invoiced_by_partner_id(self, **kw):
self.authenticate(kw)
diff --git a/indoteknik_api/models/__init__.py b/indoteknik_api/models/__init__.py
new file mode 100644
index 00000000..06f1e1da
--- /dev/null
+++ b/indoteknik_api/models/__init__.py
@@ -0,0 +1,2 @@
+from . import product_pricelist
+from . import product_template
diff --git a/indoteknik_api/models/product_pricelist.py b/indoteknik_api/models/product_pricelist.py
new file mode 100644
index 00000000..3a807f3a
--- /dev/null
+++ b/indoteknik_api/models/product_pricelist.py
@@ -0,0 +1,108 @@
+from odoo import models
+from pytz import timezone
+from datetime import datetime
+
+
+class ProductPricelist(models.Model):
+ _inherit = 'product.pricelist'
+
+ def compute_price(self, pricelist_id: int, product_id: int):
+ """
+ Compute price with tax, discount formula, and fixed_price
+ @param pricelist_id: id of product.pricelist
+ @param product_id: id of product.product
+ @return: returns price value from pricelist.
+ """
+ price = 0
+ discounts = []
+
+ is_flash_sale_product = self.is_flash_sale_product(product_id)
+ if is_flash_sale_product:
+ pricelist_id = is_flash_sale_product
+
+ is_compute_formula = True
+ while is_compute_formula:
+ pricelist = self.env['product.pricelist.item'].search([
+ ('pricelist_id', '=', pricelist_id),
+ ('product_id', '=', product_id)
+ ], limit=1)
+ if pricelist:
+ if pricelist.compute_price == 'formula':
+ pricelist_id = pricelist.base_pricelist_id.id
+ discounts.append(pricelist.price_discount)
+ else:
+ price = pricelist.fixed_price
+ is_compute_formula = False
+ else:
+ is_compute_formula = False
+
+ product = self.env['product.product'].browse(product_id)
+ if product:
+ for tax in product.taxes_id:
+ if not tax.price_include:
+ price *= (100 + tax.amount) / 100
+
+ price_discount = price
+ for discount in discounts:
+ price_discount *= (100 - discount) / 100
+
+ discount_percentage = 0
+ if len(discounts) > 0:
+ discount_percentage = (price - price_discount) / price * 100
+
+ return {
+ 'price': price,
+ 'price_discount': price_discount,
+ 'discount_percentage': discount_percentage
+ }
+
+ def get_lowest_product_variant_price(self, product_id, pricelist_id):
+ """
+ @param product_id: id of product.template
+ @param pricelist_id: id of pricelist which have default price
+ @return price: object
+ """
+ product_template = self.env['product.template'].browse(product_id)
+ product_variant_ids = [x.id for x in product_template.product_variant_ids]
+ product = self.env['product.pricelist.item'].search([
+ ('pricelist_id', '=', pricelist_id),
+ ('product_id', 'in', product_variant_ids)
+ ], order='fixed_price asc', limit=1)
+ product_id = 0
+ if product:
+ product_id = product.product_id.id
+ return self.compute_price(pricelist_id, product_id)
+
+ def get_active_flash_sale(self):
+ """
+ Check whether have active flash sale in range of date
+ @return: returns pricelist: object
+ """
+ current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ pricelist = self.search([
+ ('is_flash_sale', '=', True),
+ ('start_date', '<=', current_time),
+ ('end_date', '>=', current_time)
+ ], limit=1)
+ return pricelist
+
+ def is_flash_sale_product(self, product_id: int):
+ """
+ Check whether product is flash sale.
+ @param product_id: id of product.product
+ @return: returns pricelist_id: int, if product is flash sale.
+ """
+ pricelist = self.get_active_flash_sale()
+ if pricelist:
+ pricelist_product_ids = [x.product_id.id for x in pricelist.item_ids]
+ if product_id in pricelist_product_ids:
+ return pricelist.id
+ return False
+
+ # TODO: need testing
+ def get_active_flash_sale_items(self):
+ pricelist = self.get_active_flash_sale()
+ pricelist_item = False
+ if pricelist:
+ pricelist_item = [x.product_id for x in pricelist.item_ids]
+ return pricelist_item
diff --git a/indoteknik_api/models/product_template.py b/indoteknik_api/models/product_template.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/indoteknik_api/models/product_template.py
diff --git a/indoteknik_custom/__init__.py b/indoteknik_custom/__init__.py
index 38718f08..0650744f 100755
--- a/indoteknik_custom/__init__.py
+++ b/indoteknik_custom/__init__.py
@@ -1,2 +1 @@
from . import models
-from . import controllers \ No newline at end of file
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 937fb0c9..8d7ddeca 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -14,6 +14,8 @@
'views/blog_post.xml',
'views/coupon_program.xml',
'views/delivery_order.xml',
+ 'views/product_pricelist.xml',
+ 'views/product_pricelist_item.xml',
'views/product_public_category.xml',
'views/product_template.xml',
'views/purchase_order.xml',
diff --git a/indoteknik_custom/controllers/__init__.py b/indoteknik_custom/controllers/__init__.py
deleted file mode 100644
index 2fd318df..00000000
--- a/indoteknik_custom/controllers/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import api_controller
-from . import api
diff --git a/indoteknik_custom/controllers/api/__init__.py b/indoteknik_custom/controllers/api/__init__.py
deleted file mode 100644
index bd901bd4..00000000
--- a/indoteknik_custom/controllers/api/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import sale_order
-from . import product
diff --git a/indoteknik_custom/controllers/api/product.py b/indoteknik_custom/controllers/api/product.py
deleted file mode 100644
index 3b1c4ce8..00000000
--- a/indoteknik_custom/controllers/api/product.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import base64
-
-from .. import api_controller
-from odoo import http
-from odoo.http import request
-
-
-class ProductApi(api_controller.ApiController):
- @http.route('/api/product/search', auth='public', methods=['GET'])
- def search_product(self, **kw):
- self.authenticate(kw)
- query = kw.get('query')
- if not query:
- return self.response(code=400, description='Field query is required')
-
- query = '%' + query.replace(' ', '%') + '%'
- domain = [
- ('sale_ok', '=', True),
- '|',
- ('default_code', 'ilike', query),
- ('name', 'ilike', query)
- ]
-
- manufactures = kw.get('manufactures')
- if manufactures:
- manufactures = [int(x) for x in manufactures.split(',')]
- domain.append(('x_manufacture', 'in', manufactures))
-
- categories = kw.get('categories')
- if categories:
- categories = [int(x) for x in categories.split(',')]
- domain.append(('public_categ_ids', 'child_of', categories))
-
- product_variants = request.env['product.product'].search(domain)
- product_variant_ids = [v['id'] for v in product_variants]
-
- base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
- domain = [
- ('product_variant_ids', 'in', product_variant_ids)
- ]
- product_templates = self.search_with_api_params('product.template', kw, domain)
- data = {
- 'total_records': len(request.env['product.template'].search(domain)),
- 'products': []
- }
- for product_template in product_templates:
- stock = 0
- for product_variant in product_template.product_variant_ids:
- stock += product_variant.qty_stock_vendor
-
- discount_price = 0
- price = product_template.web_price
- if price > 0:
- if product_template.taxes_id:
- if not product_template.taxes_id.price_include:
- price += (price * product_template.taxes_id.amount / 100)
- else:
- price += (price * 11 / 100)
-
- promotion = {}
- manufacture = {}
- if product_template.x_manufacture:
- manufacture.update({
- 'id': product_template.x_manufacture.id,
- 'name': product_template.x_manufacture.x_name,
- })
- domain = [
- ('rule_products_domain', 'ilike', product_template.x_manufacture.x_name),
- ('active', '=', True)
- ]
- coupon_program = request.env['coupon.program'].search(domain, limit=1)
- if coupon_program:
- discount_price = price - (price * coupon_program.discount_percentage / 100)
- icon_1 = (base_url + 'api/image/coupon.program/x_studio_field_Ifopn/' + str(coupon_program.id)) if coupon_program.x_studio_field_Ifopn else ''
- icon_2 = (base_url + 'api/image/coupon.program/x_studio_field_2Ul77/' + str(coupon_program.id)) if coupon_program.x_studio_field_2Ul77 else ''
- promotion.update({
- 'name': coupon_program.name,
- 'discount_percentage': coupon_program.discount_percentage,
- 'icon_1': icon_1,
- 'icon_2': icon_2
- })
-
- data['products'].append({
- 'id': product_template.id,
- 'image': base_url + 'api/image/product.template/image_128/' + str(product_template.id) if product_template.image_128 else '',
- 'name': product_template.name,
- 'price': price,
- 'discount_price': discount_price,
- 'total_variant': len(product_template.product_variant_ids),
- 'stock': stock,
- 'manufacture': manufacture,
- 'promotion': promotion,
- })
-
- return self.response(data)
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index e4913205..36ff7b5e 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -24,3 +24,4 @@ from . import stock_move
from . import stock_picking
from . import stock_picking_type
from . import delivery_order
+from . import product_pricelist
diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py
new file mode 100644
index 00000000..b70eb6e6
--- /dev/null
+++ b/indoteknik_custom/models/product_pricelist.py
@@ -0,0 +1,12 @@
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+
+
+class ProductPricelist(models.Model):
+ _inherit = 'product.pricelist'
+
+ is_flash_sale = fields.Boolean(string='Flash Sale', default=False)
+ banner = fields.Binary(string='Banner')
+ start_date = fields.Datetime(string='Start Date')
+ end_date = fields.Datetime(string='End Date')
+
diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml
new file mode 100644
index 00000000..18e9835a
--- /dev/null
+++ b/indoteknik_custom/views/product_pricelist.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <record id="product_pricelist_view_inherit" model="ir.ui.view">
+ <field name="name">product.pricelist.view.inherit</field>
+ <field name="model">product.pricelist</field>
+ <field name="inherit_id" ref="product.product_pricelist_view"/>
+ <field name="arch" type="xml">
+ <field name="company_id" position="after">
+ <field name="is_flash_sale"/>
+ </field>
+ <group name="pricelist_settings" position="after">
+ <group name="flash_sale_setting">
+ <field name="banner" widget="image" attrs="{
+ 'invisible': [('is_flash_sale', '=', False)]
+ }" />
+ <field name="start_date" attrs="{
+ 'invisible': [('is_flash_sale', '=', False)],
+ 'required': [('is_flash_sale', '=', True)]
+ }" />
+ <field name="end_date" attrs="{
+ 'invisible': [('is_flash_sale', '=', False)],
+ 'required': [('is_flash_sale', '=', True)]
+ }" />
+ </group>
+ </group>
+ </field>
+ </record>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/product_pricelist_item.xml b/indoteknik_custom/views/product_pricelist_item.xml
new file mode 100755
index 00000000..94ba9e4f
--- /dev/null
+++ b/indoteknik_custom/views/product_pricelist_item.xml
@@ -0,0 +1,37 @@
+<odoo>
+ <data>
+ <record id="product_pricelist_item_action" model="ir.actions.act_window">
+ <field name="name">Product Pricelist Item</field>
+ <field name="res_model">product.pricelist.item</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <record id="product_pricelist_item_form_view_inherit" model="ir.ui.view">
+ <field name="name">product.pricelist.item.form.view.inherit</field>
+ <field name="model">product.pricelist.item</field>
+ <field name="inherit_id" ref="product.product_pricelist_item_form_view" />
+ <field name="arch" type="xml">
+ <field name="applied_on" position="before">
+ <field name="pricelist_id" />
+ </field>
+ </field>
+ </record>
+
+ <record id="product_pricelist_item_view_search_inherit" model="ir.ui.view">
+ <field name="name">product.pricelist.item.search.inherit</field>
+ <field name="model">product.pricelist.item</field>
+ <field name="inherit_id" ref="product.product_pricelist_item_view_search"/>
+ <field name="arch" type="xml">
+ <field name="pricelist_id" position="before">
+ <field name="product_id" string="Products"/>
+ </field>
+ </field>
+ </record>
+
+ <menuitem id="product_pricelist_item"
+ name="Pricelist Items"
+ parent="sale.product_menu_catalog"
+ sequence="2"
+ action="product_pricelist_item_action"/>
+ </data>
+</odoo> \ No newline at end of file