summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2024-09-20 15:06:17 +0700
committerit-fixcomart <it@fixcomart.co.id>2024-09-20 15:06:17 +0700
commit79bf9729d10c8fb37ef9071ab6df2b6644ddea49 (patch)
tree6226da5e38ad44673a1bb5d7d4e2b0fd5df1fada
parenteb7661705303a64c97e84061b53d48d5c46f6293 (diff)
parentd2bb21ae878db2a3b77dbb3341046c9d12ba1de5 (diff)
Merge branch 'production' into iman/new-register
# Conflicts: # indoteknik_custom/models/res_users.py
-rw-r--r--indoteknik_api/controllers/api_v1/cart.py22
-rw-r--r--indoteknik_api/controllers/api_v1/category_management.py3
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py28
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py84
-rw-r--r--indoteknik_api/controllers/api_v1/stock_picking.py12
-rw-r--r--indoteknik_api/controllers/api_v1/voucher.py2
-rw-r--r--indoteknik_api/models/product_product.py14
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/account_move.py2
-rw-r--r--indoteknik_custom/models/automatic_purchase.py9
-rw-r--r--indoteknik_custom/models/bill_receipt.py10
-rw-r--r--indoteknik_custom/models/commision.py1
-rw-r--r--indoteknik_custom/models/dunning_run.py11
-rw-r--r--indoteknik_custom/models/product_pricelist.py14
-rwxr-xr-xindoteknik_custom/models/product_template.py28
-rwxr-xr-xindoteknik_custom/models/purchase_order.py73
-rw-r--r--indoteknik_custom/models/purchase_order_sales_match.py12
-rwxr-xr-xindoteknik_custom/models/res_users.py1
-rwxr-xr-xindoteknik_custom/models/sale_order.py96
-rw-r--r--indoteknik_custom/models/sale_order_line.py2
-rw-r--r--indoteknik_custom/models/solr/__init__.py3
-rw-r--r--indoteknik_custom/models/solr/product_template.py36
-rw-r--r--indoteknik_custom/models/solr/promotion_program_line.py7
-rw-r--r--indoteknik_custom/models/solr/website_categories_management.py114
-rw-r--r--indoteknik_custom/models/stock_picking.py5
-rw-r--r--indoteknik_custom/models/voucher.py2
-rw-r--r--indoteknik_custom/models/website_categories_management.py36
-rw-r--r--indoteknik_custom/models/website_categories_management_line.py22
-rw-r--r--indoteknik_custom/models/website_user_cart.py20
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv1
-rw-r--r--indoteknik_custom/views/account_move.xml1
-rw-r--r--indoteknik_custom/views/bill_receipt.xml2
-rw-r--r--indoteknik_custom/views/dunning_run.xml9
-rw-r--r--indoteknik_custom/views/product_pricelist.xml14
-rwxr-xr-xindoteknik_custom/views/product_template.xml8
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml16
-rwxr-xr-xindoteknik_custom/views/sale_order.xml20
-rw-r--r--indoteknik_custom/views/website_categories_management.xml24
38 files changed, 693 insertions, 72 deletions
diff --git a/indoteknik_api/controllers/api_v1/cart.py b/indoteknik_api/controllers/api_v1/cart.py
index f472a9b0..2a24b205 100644
--- a/indoteknik_api/controllers/api_v1/cart.py
+++ b/indoteknik_api/controllers/api_v1/cart.py
@@ -16,10 +16,28 @@ class Cart(controller.Controller):
offset = int(kw.get('offset', 0))
query = [('user_id', '=', user_id)]
carts = user_cart.search(query, limit=limit, offset=offset, order='create_date desc')
- carts.write({'source': 'add_to_cart'})
+ # carts.write({'source': 'add_to_cart'})
+ products = []
+ products_inactive = []
+ for cart in carts:
+ if cart.product_id:
+ price = cart.product_id._v2_get_website_price_include_tax()
+ if cart.product_id.active and price > 0:
+ product = cart.with_context(price_for="web").get_products()
+ for product_active in product:
+ products.append(product_active)
+ else:
+ product_inactives = cart.with_context(price_for="web").get_products()
+ for inactives in product_inactives:
+ products_inactive.append(inactives)
+ else:
+ program = cart.with_context(price_for="web").get_products()
+ for programs in program:
+ products.append(programs)
data = {
'product_total': user_cart.search_count(query),
- 'products': carts.with_context(price_for="web").get_products()
+ 'products': products,
+ 'products_inactive': products_inactive
}
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/category_management.py b/indoteknik_api/controllers/api_v1/category_management.py
index 836f4493..c0ecc6b9 100644
--- a/indoteknik_api/controllers/api_v1/category_management.py
+++ b/indoteknik_api/controllers/api_v1/category_management.py
@@ -2,6 +2,7 @@ from odoo import http
from odoo.http import request
from .. import controller
+
class CategoryManagement(controller.Controller):
prefix = '/api/v1/'
@@ -42,5 +43,3 @@ class CategoryManagement(controller.Controller):
'categories': category_id2_data,
})
return self.response(data, headers=[('Cache-Control', 'max-age=3600, public')])
-
-
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index 3cc6b193..663f4d7d 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -180,4 +180,32 @@ class Partner(controller.Controller):
})
return self.response(data)
+
+ @http.route(prefix + 'check/<partner_id>/tempo', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_check_tempo_partner(self, **kw):
+ partner_id = int(kw.get('partner_id'))
+
+ partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1)
+ if not partner:
+ return self.response(code=404, description='Partner not found')
+
+ partner = partner.parent_id or partner
+
+ if any(line.days == 0 for line in partner.property_payment_term_id.line_ids):
+ return self.response(code=402, description='Partner not tempo')
+
+ result_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search([('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')]))
+
+ remaining_limit = partner.blocking_stage - result_tempo if partner.active_limit else None
+
+ data = {
+ 'name': partner.name,
+ 'amount_due': result_tempo,
+ 'remaining_limit': remaining_limit
+ }
+
+ return self.response(data)
+
+
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 7edd71f6..e7664c79 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -342,11 +342,21 @@ class SaleOrder(controller.Controller):
@http.route(prefix + 'user/<user_id>/sale_order/checkout', auth='public', method=['GET', 'OPTIONS'], csrf=False)
@controller.Controller.must_authorized(private=True, private_key='user_id')
def get_user_checkout_so(self, user_id, **kw):
- cart = request.env['website.user.cart']
+ m_voucher = request.env['voucher']
+ m_cart = request.env['website.user.cart']
+
voucher_code = kw.get('voucher')
+ voucher_shipping_code = kw.get('voucher_shipping')
source = kw.get('source')
- voucher = request.env['voucher'].search([('code', '=', voucher_code)], limit=1)
- result = cart.with_context(price_for="web").get_user_checkout(user_id, voucher, source)
+
+ voucher = m_voucher.search([('code', '=', voucher_code), ('apply_type', 'in', ['all', 'brand'])], limit=1)
+ voucher_shipping = m_voucher.search([('code', '=', voucher_shipping_code), ('apply_type', '=', 'shipping')], limit=1)
+ result = m_cart.with_context(price_for="web").get_user_checkout(
+ user_id,
+ voucher=voucher,
+ voucher_shipping=voucher_shipping,
+ source=source
+ )
return self.response(result)
@http.route(PREFIX_PARTNER + 'sale_order/checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False)
@@ -400,7 +410,7 @@ class SaleOrder(controller.Controller):
'real_invoice_id': params['value']['partner_invoice_id'],
'partner_purchase_order_name': params['value']['po_number'],
'partner_purchase_order_file': params['value']['po_file'],
- 'delivery_amt': params['value']['delivery_amount'],
+ 'delivery_amt': params['value']['delivery_amount'] * 1.10,
'estimated_arrival_days': params['value']['estimated_arrival_days'],
'shipping_cost_covered': 'customer',
'shipping_paid_by': 'customer',
@@ -455,11 +465,16 @@ class SaleOrder(controller.Controller):
sale_order.apply_promotion_program()
voucher_code = params['value']['voucher']
- voucher = request.env['voucher'].search([('code', '=', voucher_code)])
+ voucher = request.env['voucher'].search([('code', '=', voucher_code),('apply_type', 'in', ['all', 'brand'])], limit=1)
+ voucher_shipping = request.env['voucher'].search([('code', '=', voucher_code),('apply_type', 'in', ['shipping'])], limit=1)
if voucher and len(promotions) == 0:
sale_order.voucher_id = voucher.id
sale_order.apply_voucher()
+ if voucher_shipping and len(promotions) == 0:
+ sale_order.voucher_shipping_id = voucher_shipping.id
+ sale_order.apply_voucher_shipping()
+
cart_ids = [x['cart_id'] for x in carts]
if sale_order._requires_approval_margin_leader(): #jika ada error tambahkan kondisi if params['value']['type'] == 'sale_order':
sale_order.approval_status = 'pengajuan2'
@@ -621,4 +636,61 @@ class SaleOrder(controller.Controller):
}
return self.response(data)
- \ No newline at end of file
+
+ @http.route(prefix + 'tracking_order', auth='public', method=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def tracking_get_sale_order_detail(self, **kw):
+ # Extract 'so' and 'email' parameters from query parameters
+ so = kw.get('so')
+ email_user = kw.get('email')
+
+ if not email_user or not so:
+ return self.response({
+ 'code': 400,
+ 'so': so,
+ 'email': email_user,
+ 'description': "Email and Sale Order number are required."
+ })
+
+ # Search for the sale order by the name (so)
+ sale_order = request.env['sale.order'].search([('name', '=', so)], limit=1)
+ if not sale_order:
+ return self.response({
+ 'code': 404,
+ 'so': so,
+ 'email': email_user,
+ 'description': "Sale Order not found."
+ })
+
+ # Get the partner associated with the sale order
+ partner = sale_order.partner_id
+ company_id = partner.company_id.id
+
+ # Search for all partners within the same company
+ partners_in_company = request.env['res.partner'].search([('company_id', '=', company_id)])
+
+ # Check if the email matches any partner's email in the same company
+ email_match = partners_in_company.filtered(lambda p: p.email == email_user)
+ if not email_match:
+ return self.response({
+ 'code': 403,
+ 'so': so,
+ 'email': email_user,
+ 'description': "Email does not match any partner in the same company as the Sale Order."
+ })
+
+ # Check for partner child ids if needed
+ partner_child_ids = self.get_partner_child_ids(partner.id)
+ if sale_order.partner_id.id not in partner_child_ids:
+ return self.response({
+ 'code': 403,
+ 'so': so,
+ 'email': email_user,
+ 'description': "Unauthorized access to Sale Order details."
+ })
+
+ # Prepare the response data
+ data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail')
+
+ return self.response(data)
+
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py
index 5e919b31..8b941c16 100644
--- a/indoteknik_api/controllers/api_v1/stock_picking.py
+++ b/indoteknik_api/controllers/api_v1/stock_picking.py
@@ -99,3 +99,15 @@ class StockPicking(controller.Controller):
return self.response(None)
return self.response(picking.get_tracking_detail())
+
+ @http.route(prefix + 'stock-picking/<id>/tracking', auth='public', method=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_partner_stock_picking_detail_tracking_iman(self, **kw):
+ id = int(kw.get('id', 0))
+ picking_model = request.env['stock.picking']
+
+ picking = picking_model.browse(id)
+ if not picking:
+ return self.response(None)
+
+ return self.response(picking.get_tracking_detail()) \ No newline at end of file
diff --git a/indoteknik_api/controllers/api_v1/voucher.py b/indoteknik_api/controllers/api_v1/voucher.py
index cd5dff20..910488d1 100644
--- a/indoteknik_api/controllers/api_v1/voucher.py
+++ b/indoteknik_api/controllers/api_v1/voucher.py
@@ -28,7 +28,7 @@ class Voucher(controller.Controller):
domain = []
if code:
visibility.append('private')
- domain += [('code', '=', code)]
+ domain += [('code', 'ilike', code)]
user_pricelist = request.env.context.get('user_pricelist')
if user_pricelist:
domain += [('excl_pricelist_ids', 'not in', [user_pricelist.id])]
diff --git a/indoteknik_api/models/product_product.py b/indoteknik_api/models/product_product.py
index 3ecad627..386ddb6a 100644
--- a/indoteknik_api/models/product_product.py
+++ b/indoteknik_api/models/product_product.py
@@ -234,19 +234,17 @@ class ProductProduct(models.Model):
('pricelist_id', '=', int(product_pricelist_tier)),
('product_id', '=', self.id)
], limit=1)
- if pricelist_item:
- # base_price = self._get_website_price_exclude_tax()
- base_price_incl = self._get_website_price_include_tax()
- if tier_number in ['1_v2', '2_v2', '3_v2', '4_v2', '5_v2']:
- base_price_incl = self._v2_get_website_price_include_tax()
-
+ base_price_incl = self._get_website_price_include_tax()
+ if tier_number in ['1_v2', '2_v2', '3_v2', '4_v2', '5_v2']:
+ base_price_incl = self._v2_get_website_price_include_tax()
+ if pricelist_item and base_price_incl:
discount = pricelist_item.price_discount
price = base_price_incl - (base_price_incl * discount / 100)
price = price / default_divide_tax
price = math.floor(price)
data = {
- f'discount_tier{tier_number}': discount or 0,
- f'price_tier{tier_number}': price or 0
+ f'discount_tier{tier_number}': discount if base_price_incl else 0,
+ f'price_tier{tier_number}': price if base_price_incl else 0,
}
return data
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index e9ce587c..ad7b1d09 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -39,6 +39,7 @@ from . import website_brand_homepage
from . import website_categories_homepage
from . import website_categories_lob
from . import website_categories_management
+from . import website_categories_management_line
from . import website_content
from . import website_page_content
from . import website_user_cart
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index a7010bbe..725b3c2d 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -174,7 +174,7 @@ class AccountMove(models.Model):
def _compute_mark_upload_efaktur(self):
for move in self:
- if move.date_efaktur_exported or move.is_efaktur_exported or move.efaktur_document:
+ if move.efaktur_document:
move.mark_upload_efaktur = 'sudah_upload'
else:
move.mark_upload_efaktur = 'belum_upload'
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index 3561fc0f..548115e6 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -220,6 +220,7 @@ class AutomaticPurchase(models.Model):
# i start from zero (0)
for i in range(page):
new_po = self.env['purchase.order'].create([param_header])
+ new_po.payment_term_id = new_po.partner_id.property_supplier_payment_term_id
new_po.name = new_po.name + name + str(i + 1)
self.env['automatic.purchase.match'].create([{
@@ -261,7 +262,7 @@ class AutomaticPurchase(models.Model):
new_po_line = self.env['purchase.order.line'].create([param_line])
line.current_po_id = new_po.id
line.current_po_line_id = new_po_line.id
- self.update_purchase_price_so_line(line)
+ # self.update_purchase_price_so_line(line)
self.create_purchase_order_sales_match(new_po)
@@ -292,6 +293,9 @@ class AutomaticPurchase(models.Model):
sale_ids_set.add(sale_id_with_salesperson)
sale_ids_name.add(sale_order.sale_id.name)
+ margin_item = sale_order.sale_line_id.item_margin / sale_order.qty_so if sale_order.qty_so else 0
+ margin_item = margin_item * sale_order.qty_po
+
matches_so_line = {
'purchase_order_id': purchase_order.id,
'sale_id': sale_order.sale_id.id,
@@ -304,7 +308,8 @@ class AutomaticPurchase(models.Model):
'product_id': sale_order.product_id.id,
'qty_so': sale_order.qty_so,
'qty_po': sale_order.qty_po,
- 'margin_so': sale_order.sale_line_id.item_percent_margin
+ 'margin_so': sale_order.sale_line_id.item_percent_margin,
+ 'margin_item': margin_item
}
po_matches_so_line = self.env['purchase.order.sales.match'].create([matches_so_line])
diff --git a/indoteknik_custom/models/bill_receipt.py b/indoteknik_custom/models/bill_receipt.py
index 76449c1f..7d38d5ad 100644
--- a/indoteknik_custom/models/bill_receipt.py
+++ b/indoteknik_custom/models/bill_receipt.py
@@ -22,8 +22,16 @@ class BillReceipt(models.Model):
resi_tukar_faktur = fields.Char(string='Resi Faktur')
date_terima_tukar_faktur = fields.Date(string='Terima Faktur')
shipper_faktur_id = fields.Many2one('delivery.carrier', string='Shipper Faktur')
- is_validated = fields.Boolean(string='Validated')
+ is_validated = fields.Boolean(string='Validated')
notification = fields.Char(string='Notification')
+ grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total")
+
+ def _compute_grand_total(self):
+ for record in self:
+ grand_total = 0
+ for line in record.bill_line:
+ grand_total += line.total_amt
+ record.grand_total = grand_total
def copy_date_faktur(self):
if not self.is_validated:
diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py
index c5809005..48f1c7f6 100644
--- a/indoteknik_custom/models/commision.py
+++ b/indoteknik_custom/models/commision.py
@@ -261,6 +261,7 @@ class CustomerCommision(models.Model):
('move_id.invoice_date', '>=', self.date_from),
('move_id.invoice_date', '<=', self.date_to),
('product_id.x_manufacture', 'in', brand),
+ ('exclude_from_invoice_tab', '=', False),
]
invoice_lines = self.env['account.move.line'].search(where, order='id')
for invoice_line in invoice_lines:
diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py
index d75d7c51..c167aab7 100644
--- a/indoteknik_custom/models/dunning_run.py
+++ b/indoteknik_custom/models/dunning_run.py
@@ -27,6 +27,17 @@ class DunningRun(models.Model):
shipper_faktur_id = fields.Many2one('delivery.carrier', string='Shipper Faktur')
is_validated = fields.Boolean(string='Validated')
notification = fields.Char(string='Notification')
+ is_paid = fields.Boolean(string='Paid')
+ description = fields.Char(string='Description')
+ comment = fields.Char(string='Comment')
+ grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total")
+
+ def _compute_grand_total(self):
+ for record in self:
+ grand_total = 0
+ for line in record.dunning_line:
+ grand_total += line.total_amt
+ record.grand_total = grand_total
def copy_date_faktur(self):
if not self.is_validated:
diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py
index 2b17cf6e..c299ff2f 100644
--- a/indoteknik_custom/models/product_pricelist.py
+++ b/indoteknik_custom/models/product_pricelist.py
@@ -1,5 +1,5 @@
from odoo import models, fields
-from datetime import datetime
+from datetime import datetime, timedelta
class ProductPricelist(models.Model):
@@ -18,6 +18,18 @@ class ProductPricelist(models.Model):
banner_top = fields.Binary(string='Banner Top')
flashsale_tag = fields.Char(string='Flash Sale Tag')
+ def _check_end_date_and_update_solr(self):
+ today = datetime.utcnow().date()
+ one_day_ago = today - timedelta(days=1)
+
+ pricelists = self.search([
+ ('is_flash_sale', '=', True)
+ ])
+
+ for pricelist in pricelists:
+ if fields.Date.to_date(pricelist.end_date) == one_day_ago:
+ pricelist._constrains_related_solr_field()
+
def _remaining_time_in_second(self):
if not self.end_date:
return 0
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index d51f903f..031d1b5b 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -61,6 +61,12 @@ class ProductTemplate(models.Model):
tkdn = fields.Boolean(string='TKDN')
short_spesification = fields.Char(string='Short Spesification')
+ @api.constrains('name', 'internal_reference', 'x_manufacture')
+ def required_public_categ_ids(self):
+ for rec in self:
+ if not rec.public_categ_ids:
+ raise UserError('Field Categories harus diisi')
+
def _get_qty_sold(self):
for rec in self:
rec.qty_sold = sum(x.qty_sold for x in rec.product_variant_ids)
@@ -332,9 +338,9 @@ class ProductTemplate(models.Model):
return values
def write(self, vals):
- for rec in self:
- if rec.id == 224484:
- raise UserError('Tidak dapat mengubah produk sementara')
+ # for rec in self:
+ # if rec.id == 224484:
+ # raise UserError('Tidak dapat mengubah produk sementara')
return super(ProductTemplate, self).write(vals)
@@ -367,6 +373,12 @@ class ProductProduct(models.Model):
qty_sold = fields.Float(string='Sold Quantity', compute='_get_qty_sold')
short_spesification = fields.Char(string='Short Spesification')
+ @api.constrains('name', 'internal_reference', 'x_manufacture')
+ def required_public_categ_ids(self):
+ for rec in self:
+ if not rec.public_categ_ids:
+ raise UserError('Field Categories harus diisi')
+
@api.constrains('active')
def archive_product(self):
for product in self:
@@ -376,8 +388,16 @@ class ProductProduct(models.Model):
if product_template.active and product.active:
if not product.active and len(variants) == 1:
product_template.with_context(skip_active_constraint=True).active = False
+ product_template.unpublished = True
elif not product.active and len(variants) > 1:
- continue
+ all_inactive = all(not variant.active for variant in variants)
+ if all_inactive:
+ product_template.with_context(skip_active_constraint=True).active = False
+ product_template.unpublished = True
+ else:
+ continue
+ if any(variant.active for variant in variants):
+ product_template.unpublished = False
def update_internal_reference_variants(self, limit=100):
variants = self.env['product.product'].search([
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 3a11ab1e..8a47482a 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -31,6 +31,7 @@ class PurchaseOrder(models.Model):
('approved', 'Approved'),
], string='Approval Status', readonly=True, copy=False, index=True, tracking=3)
delivery_amount = fields.Float('Delivery Amount', compute='compute_delivery_amount')
+ delivery_amt = fields.Float('Delivery Amt')
total_margin = fields.Float(
'Margin', compute='compute_total_margin',
help="Total Margin in Sales Order Header")
@@ -66,6 +67,72 @@ class PurchaseOrder(models.Model):
('printed', 'Printed')
], string='Printed?', copy=False, tracking=True)
date_done_picking = fields.Datetime(string='Date Done Picking', compute='get_date_done')
+ bills_dp_id = fields.Many2one('account.move', string='Bills DP')
+ grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total')
+ total_margin_match = fields.Float(string='Total Margin Match', compute='_compute_total_margin_match')
+
+ def _compute_total_margin_match(self):
+ for purchase in self:
+ match = self.env['purchase.order.sales.match']
+ result = match.read_group(
+ [('purchase_order_id', '=', purchase.id)],
+ ['margin_item'],
+ []
+ )
+ purchase.total_margin_match = result[0].get('margin_item', 0.0)
+
+ def _compute_grand_total(self):
+ for order in self:
+ if order.delivery_amt:
+ order.grand_total = order.delivery_amt + order.amount_total
+ else:
+ order.grand_total = order.amount_total
+
+ def create_bill_dp(self):
+ if not self.env.user.is_accounting:
+ raise UserError('Hanya Accounting yang bisa bikin bill dp')
+
+ current_date = datetime.utcnow()
+ data_bills = {
+ 'partner_id': self.partner_id.id,
+ 'partner_shipping_id': self.partner_id.id,
+ 'ref': self.name,
+ 'invoice_date': current_date,
+ 'date': current_date,
+ 'move_type': 'in_invoice'
+
+ }
+
+ bills = self.env['account.move'].create([data_bills])
+
+ product_dp = self.env['product.product'].browse(229625)
+
+ data_line_bills = {
+ 'move_id': bills.id,
+ 'product_id': product_dp.id, #product down payment
+ 'account_id': 401, #Uang Muka persediaan barang dagang
+ 'quantity': 1,
+ 'product_uom_id': 1,
+ 'tax_ids': [line[0].taxes_id.id for line in self.order_line],
+ }
+
+
+ bills_line = self.env['account.move.line'].create([data_line_bills])
+
+ self.bills_dp_id = bills.id
+
+ move_line = bills.line_ids
+ move_line.name = '[IT.121456] Down Payment'
+ move_line.partner_id = self.partner_id.id
+
+ return {
+ 'name': _('Account Move'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'account.move',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', '=', bills.id)]
+ }
def get_date_done(self):
picking = self.env['stock.picking'].search([
@@ -435,7 +502,7 @@ class PurchaseOrder(models.Model):
res = super(PurchaseOrder, self).button_confirm()
current_time = datetime.now()
self.check_ppn_mix()
- self.check_data_vendor()
+ # self.check_data_vendor()
if self.total_percent_margin < self.total_so_percent_margin and not self.env.user.is_purchasing_manager and not self.env.user.is_leader:
raise UserError("Beda Margin dengan Sales, harus approval Manager")
@@ -624,6 +691,8 @@ class PurchaseOrder(models.Model):
purchase_price = line.price_subtotal
if line.order_id.delivery_amount > 0:
purchase_price += line.delivery_amt_line
+ if line.order_id.delivery_amt > 0:
+ purchase_price += line.order_id.delivery_amt
real_item_margin = sales_price - purchase_price
sum_margin += real_item_margin
@@ -666,6 +735,8 @@ class PurchaseOrder(models.Model):
purchase_price = po_line.price_subtotal / po_line.product_qty * line.qty_po
if line.purchase_order_id.delivery_amount > 0:
purchase_price += (po_line.delivery_amt_line / po_line.product_qty) * line.qty_po
+ if line.purchase_order_id.delivery_amt > 0:
+ purchase_price += line.purchase_order_id.delivery_amt
real_item_margin = sales_price - purchase_price
sum_margin += real_item_margin
diff --git a/indoteknik_custom/models/purchase_order_sales_match.py b/indoteknik_custom/models/purchase_order_sales_match.py
index 78581409..d1d929d3 100644
--- a/indoteknik_custom/models/purchase_order_sales_match.py
+++ b/indoteknik_custom/models/purchase_order_sales_match.py
@@ -20,7 +20,17 @@ class PurchaseOrderSalesMatch(models.Model):
product_id = fields.Many2one('product.product', string='Product')
qty_so = fields.Float(string='Qty SO')
qty_po = fields.Float(string='Qty PO')
- margin_so = fields.Float(string='Margin SO')
+ margin_so = fields.Float(string='Margin SO')
+ margin_item = fields.Float(string='Margin')
+ delivery_amt = fields.Float(string='Delivery Amount', compute='_compute_delivery_amt')
+ margin_deduct = fields.Float(string='After Deduct', compute='_compute_delivery_amt')
+
+ def _compute_delivery_amt(self):
+ for line in self:
+ percent_margin = line.margin_item / line.purchase_order_id.total_margin_match \
+ if line.purchase_order_id.total_margin_match else 0
+ line.delivery_amt = line.purchase_order_id.delivery_amt * percent_margin
+ line.margin_deduct = line.margin_item - line.delivery_amt
@api.onchange('sale_id')
def onchange_sale_id(self):
diff --git a/indoteknik_custom/models/res_users.py b/indoteknik_custom/models/res_users.py
index cedf19bd..d8eb348a 100755
--- a/indoteknik_custom/models/res_users.py
+++ b/indoteknik_custom/models/res_users.py
@@ -11,6 +11,7 @@ class ResUsers(models.Model):
activation_token = fields.Char(string="Activation Token")
otp_code = fields.Char(string='OTP Code')
otp_create_date = fields.Datetime(string='OTP Create Date')
+ payment_terms_id = fields.Many2one('account.payment.term', related='partner_id.property_payment_term_id', string='Payment Terms')
user_company_name = fields.Char(string="User Company Name")
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 16a1f415..aa5443c5 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -88,6 +88,8 @@ class SaleOrder(models.Model):
voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher', copy=False)
applied_voucher_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False)
amount_voucher_disc = fields.Float(string='Voucher Discount')
+ applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False)
+ amount_voucher_shipping_disc = fields.Float(string='Voucher Discount')
source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True)
estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0)
email = fields.Char(string='Email')
@@ -109,6 +111,45 @@ class SaleOrder(models.Model):
date_driver_departure = fields.Datetime(string='Departure Date', compute='_compute_date_kirim', copy=False)
note_website = fields.Char(string="Note Website")
use_button = fields.Boolean(string='Using Calculate Selling Price', copy=False)
+ voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Voucher Shipping', copy=False)
+ margin_after_delivery_purchase = fields.Float(string='Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase')
+ percent_margin_after_delivery_purchase = fields.Float(string='% Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase')
+ purchase_delivery_amt = fields.Float(string='Purchase Delivery Amount', compute='_compute_purchase_delivery_amount')
+ type_promotion = fields.Char(string='Type Promotion', compute='_compute_type_promotion')
+
+ def _compute_type_promotion(self):
+ for rec in self:
+ promotion_types = []
+ for promotion in rec.order_promotion_ids:
+ for line_program in promotion.program_line_id:
+ if line_program.promotion_type:
+ promotion_types.append(dict(line_program._fields['promotion_type'].selection).get(line_program.promotion_type))
+
+ # Removing duplicates by converting to a set, then back to a list
+ rec.type_promotion = ', '.join(sorted(set(promotion_types)))
+
+ def _compute_purchase_delivery_amount(self):
+ for order in self:
+ match = self.env['purchase.order.sales.match']
+ result2 = match.search([
+ ('sale_id.id', '=', order.id)
+ ])
+ delivery_amt = 0
+ for res in result2:
+ delivery_amt = res.delivery_amt
+ order.purchase_delivery_amt = delivery_amt
+
+ def _compute_margin_after_delivery_purchase(self):
+ for order in self:
+ order.margin_after_delivery_purchase = order.total_margin - order.purchase_delivery_amt
+ if order.amount_untaxed == 0:
+ order.percent_margin_after_delivery_purchase = 0
+ continue
+ if order.shipping_cost_covered == 'indoteknik':
+ delivery_amt = order.delivery_amt
+ else:
+ delivery_amt = 0
+ order.percent_margin_after_delivery_purchase = round((order.margin_after_delivery_purchase / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2)
def _compute_date_kirim(self):
for rec in self:
@@ -630,6 +671,9 @@ class SaleOrder(models.Model):
if not order.client_order_ref and order.create_date > datetime(2024, 6, 27):
raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO")
+
+ if not order.commitment_date and order.create_date > datetime(2024, 9, 12):
+ raise UserError("Expected Delivery Date kosong, wajib diisi")
if order.validate_partner_invoice_due():
return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension')
@@ -743,7 +787,7 @@ class SaleOrder(models.Model):
delivery_amt = order.delivery_amt
else:
delivery_amt = 0
- order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt)) * 100, 2)
+ order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2)
# order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2)
@api.onchange('sales_tax_id')
@@ -780,6 +824,28 @@ class SaleOrder(models.Model):
self.apply_voucher()
+ def action_apply_voucher_shipping(self):
+ for line in self.order_line:
+ if line.order_promotion_id:
+ raise UserError('Voucher tidak dapat digabung dengan promotion program')
+
+ voucher = self.voucher_shipping_id
+ if voucher.limit > 0 and voucher.count_order >= voucher.limit:
+ raise UserError('Voucher tidak dapat digunakan karena sudah habis digunakan')
+
+ partner_voucher_orders = []
+ for order in voucher.order_ids:
+ if order.partner_id.id == self.partner_id.id:
+ partner_voucher_orders.append(order)
+
+ if voucher.limit_user > 0 and len(partner_voucher_orders) >= voucher.limit_user:
+ raise UserError('Voucher tidak dapat digunakan karena Customer ini sudah menghabiskan kuota voucher')
+
+ if self.pricelist_id.id in [x.id for x in voucher.excl_pricelist_ids]:
+ raise UserError('Voucher tidak dapat digunakan karena pricelist ini tidak berlaku pada voucher')
+
+ self.apply_voucher_shipping()
+
def apply_voucher(self):
order_line = []
for line in self.order_line:
@@ -821,6 +887,29 @@ class SaleOrder(models.Model):
self.amount_voucher_disc = voucher['discount']['all']
self.applied_voucher_id = self.voucher_id
+ def apply_voucher_shipping(self):
+ for order in self:
+ delivery_amt = order.delivery_amt
+ voucher = order.voucher_shipping_id
+
+ if voucher:
+ max_discount_amount = voucher.discount_amount
+ voucher_type = voucher.discount_type
+
+ if voucher_type == 'fixed_price':
+ discount = max_discount_amount
+ elif voucher_type == 'percentage':
+ discount = delivery_amt * (max_discount_amount / 100)
+
+ delivery_amt -= discount
+
+ delivery_amt = max(delivery_amt, 0)
+
+ order.delivery_amt = delivery_amt
+
+ order.amount_voucher_shipping_disc = discount
+ order.applied_voucher_shipping_id = order.voucher_id.id
+
def cancel_voucher(self):
self.applied_voucher_id = False
self.amount_voucher_disc = 0
@@ -829,6 +918,11 @@ class SaleOrder(models.Model):
line.discount = line.initial_discount
line.initial_discount = False
+ def cancel_voucher_shipping(self):
+ self.delivery_amt + self.amount_voucher_shipping_disc
+ self.applied_voucher_shipping_id = False
+ self.amount_voucher_shipping_disc = 0
+
def action_web_approve(self):
if self.env.uid != self.partner_id.user_id.id:
raise UserError('You are not authorized to approve this order. Only %s can approve this order.' % self.partner_id.user_id.name)
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index a64a744c..50438dbe 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -348,7 +348,7 @@ class SaleOrderLine(models.Model):
def validate_line(self):
for line in self:
- if line.product_id.id in [385544, 224484]:
+ if line.product_id.id in [385544, 224484, 417724]:
raise UserError('Produk Sementara Tidak Bisa Di Confirm atau Ask Approval')
if not line.product_id or line.product_id.type == 'service':
continue
diff --git a/indoteknik_custom/models/solr/__init__.py b/indoteknik_custom/models/solr/__init__.py
index 606c0035..dafd5a1e 100644
--- a/indoteknik_custom/models/solr/__init__.py
+++ b/indoteknik_custom/models/solr/__init__.py
@@ -10,4 +10,5 @@ from . import x_banner_banner
from . import product_public_category
from . import x_banner_category
from . import promotion_program
-from . import promotion_program_line \ No newline at end of file
+from . import promotion_program_line
+from . import website_categories_management \ No newline at end of file
diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py
index 6f76c529..d8dec47c 100644
--- a/indoteknik_custom/models/solr/product_template.py
+++ b/indoteknik_custom/models/solr/product_template.py
@@ -1,4 +1,5 @@
from datetime import datetime
+from bs4 import BeautifulSoup
from odoo import api, fields, models
@@ -78,6 +79,9 @@ class ProductTemplate(models.Model):
is_in_bu = bool(stock_quant)
+ cleaned_desc = BeautifulSoup(template.website_description or '', "html.parser").get_text()
+ website_description = template.website_description if cleaned_desc else ''
+
document = solr_model.get_doc('product', template.id)
document.update({
"id": template.id,
@@ -101,9 +105,11 @@ class ProductTemplate(models.Model):
"search_rank_i": template.search_rank,
"search_rank_weekly_i": template.search_rank_weekly,
"category_id_ids": category_ids, # ID kategori sebagai string yang dipisahkan koma
+ "category_parent_ids": self.get_category_hierarchy_ids(category_ids), # ID kategori sebagai string yang dipisahkan koma
"category_name_s": ', '.join(category_names), # Nama kategori sebagai string yang dipisahkan koma
"category_name": category_names, # Nama kategori sebagai list
"description_t": template.website_description or '',
+ "description_clean_t": cleaned_desc or '',
'has_product_info_b': True,
'publish_b': not template.unpublished,
'sni_b': template.unpublished,
@@ -127,6 +133,36 @@ class ProductTemplate(models.Model):
if not document.get('has_price_info_b'):
template._sync_price_to_solr()
+ def get_category_hierarchy_ids(self, category_id):
+ """
+ Function to get category hierarchy IDs including the category itself and its parents.
+
+ Args:
+ category_id (int): The ID of the category you want to retrieve.
+ env (Environment): The Odoo environment.
+
+ Returns:
+ list: A list of IDs for the category and its parents.
+ """
+ category_ids = []
+
+ def traverse_category(cat_id):
+ # Retrieve category object based on ID
+ category = self.env['product.public.category'].browse(cat_id)
+
+ # Add the current category ID to the list
+ category_ids.append(category.id)
+
+ # If there is a parent category, traverse upwards
+ if category.parent_id:
+ traverse_category(category.parent_id.id)
+
+ # Start traversal from the initial category
+ traverse_category(category_id)
+
+ # Reverse the list to get the hierarchy from top level to the current level
+ return list(reversed(category_ids))
+
def _sync_price_to_solr(self):
solr_model = self.env['apache.solr']
TIER_NUMBERS = ['1_v2', '2_v2', '3_v2', '4_v2', '5_v2']
diff --git a/indoteknik_custom/models/solr/promotion_program_line.py b/indoteknik_custom/models/solr/promotion_program_line.py
index 4b0e67f6..3e3a2a28 100644
--- a/indoteknik_custom/models/solr/promotion_program_line.py
+++ b/indoteknik_custom/models/solr/promotion_program_line.py
@@ -37,6 +37,9 @@ class PromotionProgramLine(models.Model):
promotion_type = rec._res_promotion_type()
+ # Gathering all categories
+ category_names = [category.name for category in rec.product_ids.product_id.public_categ_ids]
+
# Set sequence_i to None if rec.sequence is 0
sequence_value = None if rec.sequence == 0 else rec.sequence
@@ -57,7 +60,9 @@ class PromotionProgramLine(models.Model):
'free_products_s': json.dumps(free_products),
'total_qty_i': sum([x.qty for x in rec.product_ids] + [x.qty for x in rec.free_product_ids]),
'total_qty_sold_f': [x.product_id.qty_sold for x in rec.product_ids],
- 'active_b': rec.active
+ 'active_b': rec.active,
+ "manufacture_name_s": rec.product_ids.product_id.x_manufacture.x_name or '',
+ "category_name": category_names,
})
self.solr().add([document])
diff --git a/indoteknik_custom/models/solr/website_categories_management.py b/indoteknik_custom/models/solr/website_categories_management.py
new file mode 100644
index 00000000..0a40a356
--- /dev/null
+++ b/indoteknik_custom/models/solr/website_categories_management.py
@@ -0,0 +1,114 @@
+from odoo import models, fields, api
+from datetime import datetime
+import json
+
+class WebsiteCategoriesHomepage(models.Model):
+ _inherit = 'website.categories.management'
+
+ last_update_solr = fields.Datetime('Last Update Solr')
+
+ def solr(self):
+ """Returns the Solr connection object."""
+ return self.env['apache.solr'].connect('category_management')
+
+ def update_last_update_solr(self):
+ """Updates the last sync time for the record."""
+ self.last_update_solr = datetime.utcnow()
+
+ def _create_solr_queue(self, function_name):
+ """Creates unique Solr queue for each record."""
+ for rec in self:
+ self.env['apache.solr.queue'].create_unique({
+ 'res_model': self._name,
+ 'res_id': rec.id,
+ 'function_name': function_name
+ })
+
+ @api.constrains('status')
+ def _create_solr_queue_sync_status(self):
+ """Triggers Solr sync when the status changes."""
+ self._create_solr_queue('_sync_status_category_homepage_solr')
+
+ @api.constrains('category_id', 'category_id2', 'sequence')
+ def _create_solr_queue_sync_category_homepage(self):
+ """Triggers Solr sync when categories or sequence change."""
+ self._create_solr_queue('_sync_category_management_to_solr')
+
+ def action_sync_to_solr(self):
+ """Manual action to sync selected categories to Solr."""
+ category_ids = self.env.context.get('active_ids', [])
+ categories = self.search([('id', 'in', category_ids)])
+ categories._create_solr_queue('_sync_category_management_to_solr')
+
+ def unlink(self):
+ """Overrides unlink method to remove records from Solr."""
+ for rec in self:
+ self.solr().delete(rec.id)
+ self.solr().optimize()
+ self.solr().commit()
+ return super(WebsiteCategoriesHomepage, self).unlink()
+
+ def _sync_status_category_homepage_solr(self):
+ """Syncs status to Solr or deletes if not active."""
+ for rec in self:
+ if rec.status == 'tayang':
+ rec._sync_category_management_to_solr()
+ else:
+ rec.unlink()
+
+ def _sync_category_management_to_solr(self):
+ """Syncs categories (Level 1, 2, and 3) to Solr."""
+ solr_model = self.env['apache.solr']
+
+ for category in self:
+ if category.status != 'tayang':
+ continue
+
+ # Prepare Level 1 document
+ document = {
+ 'id': category.id,
+ 'sequence_i': category.sequence or '',
+ 'category_id_i': category.category_id.id,
+ 'name_s': category.category_id.name,
+ 'numFound_i': len(category.category_id.product_tmpl_ids),
+ 'image_s': self.env['ir.attachment'].api_image(
+ 'product.public.category', 'image_1920', category.category_id.id
+ ),
+ 'categories': []
+ }
+
+ # Prepare Level 2 documents
+ for category_level_2 in category.line_ids.mapped('category_id2'):
+ level_2_doc = {
+ 'id_level_2': category_level_2.id,
+ 'name': category_level_2.name,
+ 'numFound': len(category_level_2.product_tmpl_ids),
+ 'image': self.env['ir.attachment'].api_image(
+ 'product.public.category', 'image_1920', category_level_2.id
+ ),
+ 'child_frontend_id_i': []
+ }
+
+ # Prepare Level 3 documents
+ for category_level_3 in category_level_2.child_frontend_id2:
+ level_3_doc = {
+ 'id_level_3': category_level_3.id,
+ 'name': category_level_3.name,
+ 'numFound': len(category_level_3.product_tmpl_ids),
+ 'image': self.env['ir.attachment'].api_image(
+ 'product.public.category', 'image_1920', category_level_3.id
+ ),
+ }
+ level_2_doc['child_frontend_id_i'].append(json.dumps(level_3_doc))
+
+ # Add Level 2 document to Level 1
+ document['categories'].append(json.dumps(level_2_doc))
+
+ # Sync document with Solr
+ self.solr().add([document])
+ category.update_last_update_solr()
+
+ # Commit and optimize Solr changes
+ self.solr().commit()
+ self.solr().optimize()
+
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 6083e9e7..3dd960e2 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -126,6 +126,8 @@ class StockPicking(models.Model):
def do_unreserve(self):
res = super(StockPicking, self).do_unreserve()
+ if not self.env.user.is_purchasing_manager:
+ raise UserError('Hanya Purchasing Manager yang bisa Unreserve')
current_time = datetime.datetime.utcnow()
self.date_unreserve = current_time
return res
@@ -371,6 +373,9 @@ class StockPicking(models.Model):
if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver:
raise UserError("Harus di Approve oleh Logistik")
+
+ if self.location_dest_id.id == 47 and not self.env.user.is_purchasing_manager:
+ raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara")
if self.group_id.sale_id:
if self.group_id.sale_id.payment_link_midtrans:
diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py
index f7305999..37c97338 100644
--- a/indoteknik_custom/models/voucher.py
+++ b/indoteknik_custom/models/voucher.py
@@ -146,7 +146,7 @@ class Voucher(models.Model):
def calc_discount_amount(self, total):
result = { 'all': 0, 'brand': {} }
- if self.apply_type == 'all':
+ if self.apply_type in ['all', 'shipping']:
if total['all'] < self.min_purchase_amount:
return result
diff --git a/indoteknik_custom/models/website_categories_management.py b/indoteknik_custom/models/website_categories_management.py
index 208b07a2..e430ef5f 100644
--- a/indoteknik_custom/models/website_categories_management.py
+++ b/indoteknik_custom/models/website_categories_management.py
@@ -6,8 +6,8 @@ class WebsiteCategoriesManagement(models.Model):
_rec_name = 'category_id'
category_id = fields.Many2one('product.public.category', string='Category Level 1', help='table ecommerce category', domain=lambda self: self._get_default_category_domain())
- category_id2 = fields.Many2many(comodel_name='product.public.category', relation='website_categories_category_id2_rel',column1='website_categories_homepage_id', column2='product_public_category_id', string='Category Level 2', copy=False)
sequence = fields.Integer(string='Sequence')
+ line_ids = fields.One2many('website.categories.management.line', 'management_id', string='Category Level 2 Lines', auto_join=True)
status = fields.Selection([
('tayang', 'Tayang'),
('tidak_tayang', 'Tidak Tayang')
@@ -17,11 +17,11 @@ class WebsiteCategoriesManagement(models.Model):
def _onchange_category_id(self):
domain = {}
if self.category_id != self._origin.category_id: # Check if the category_id has changed
- self.category_id2 = [(5, 0, 0)] # Clear the category_id2 field if category_id has changed
+ self.line_ids = [(5, 0, 0)] # Clear the lines if category_id has changed
if self.category_id:
- domain['category_id2'] = [('parent_frontend_id', '=', self.category_id.id)]
+ domain['line_ids'] = [('parent_frontend_id', '=', self.category_id.id)]
else:
- domain['category_id2'] = []
+ domain['line_ids'] = []
return {'domain': domain}
@@ -42,24 +42,30 @@ class WebsiteCategoriesManagement(models.Model):
def _check_category_consistency(self):
for record in self:
- category_ids = record.category_id2.ids
- for category in record.category_id2:
- for child_category in category.child_frontend_id2:
- if child_category.parent_frontend_id.id not in category_ids:
+ category_level2_ids = record.line_ids.mapped('category_id2.id') # Get all Category Level 2 IDs
+ for line in record.line_ids:
+ for category_level3 in line.category_id3_ids: # Loop through selected Category Level 3
+ if category_level3.parent_frontend_id.id not in category_level2_ids:
raise ValidationError(
- f"Category Level 3 {child_category.name} bukan bagian dari category Level 2 {category.name}")
+ f"Category Level 3 '{category_level3.name}' bukan bagian dari Category Level 2 '{line.category_id2.name}'")
def unlink(self):
- for record in self.category_id2:
- if record.id:
+ for record in self.line_ids:
+ if record.category_id2:
related_categories = self.env['product.public.category'].search([
- ('id', 'in', record.ids)
+ ('id', '=', record.category_id2.id)
])
for category in related_categories:
- for category3 in record.child_frontend_id2.ids:
- if category3 in category.child_frontend_id2.ids:
+ # Iterate through the Category Level 3 related to the current Category Level 2
+ for category3 in record.category_id3_ids:
+ # If Category Level 3 is linked to Category Level 2, remove the link
+ if category3.id in category.child_frontend_id2.ids:
category.write({
- 'child_frontend_id2': [(3, category3)]
+ 'child_frontend_id2': [(3, category3.id)]
+ # Remove the link between Category Level 2 and Category Level 3
})
+
return super(WebsiteCategoriesManagement, self).unlink()
+
+
diff --git a/indoteknik_custom/models/website_categories_management_line.py b/indoteknik_custom/models/website_categories_management_line.py
new file mode 100644
index 00000000..2f97ddfa
--- /dev/null
+++ b/indoteknik_custom/models/website_categories_management_line.py
@@ -0,0 +1,22 @@
+from odoo import fields, models, api
+from odoo.exceptions import ValidationError
+
+class WebsiteCategoriesManagementLine(models.Model):
+ _name = 'website.categories.management.line'
+ _order = 'sequence'
+
+ sequence = fields.Integer(string='Sequence')
+ management_id = fields.Many2one('website.categories.management', string='Management Reference', required=True, ondelete='cascade')
+ category_id2 = fields.Many2one('product.public.category', string='Category Level 2', required=True,)
+ category_id3_ids = fields.Many2many('product.public.category', string='Category Level 3')
+
+ @api.onchange('category_id2')
+ def _onchange_category_id2(self):
+ """ Update domain for category_id3_ids based on category_id2 """
+ if self.category_id2:
+ domain_category_id3_ids = [('parent_frontend_id', '=', self.category_id2.id)]
+ else:
+ domain_category_id3_ids = []
+
+ return {'domain': {'category_id3_ids': domain_category_id3_ids}}
+
diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py
index d9352abb..b0cf47f7 100644
--- a/indoteknik_custom/models/website_user_cart.py
+++ b/indoteknik_custom/models/website_user_cart.py
@@ -91,7 +91,7 @@ class WebsiteUserCart(models.Model):
products = carts.get_products()
return products
- def get_user_checkout(self, user_id, voucher=False, source=False):
+ def get_user_checkout(self, user_id, voucher=False, voucher_shipping=False, source=False):
products = self.get_product_by_user(user_id=user_id, selected=True, source=source)
total_purchase = 0
@@ -109,12 +109,12 @@ class WebsiteUserCart(models.Model):
subtotal = total_purchase - total_discount
discount_voucher = 0
- if voucher:
- order_line = []
+ discount_voucher_shipping = 0
+ order_line = []
+
+ if voucher or voucher_shipping:
for product in products:
- if product['cart_type'] == 'promotion':
- continue
-
+ if product['cart_type'] == 'promotion': continue
order_line.append({
'product_id': self.env['product.product'].browse(product['id']),
'price': product['price']['price'],
@@ -122,9 +122,16 @@ class WebsiteUserCart(models.Model):
'qty': product['quantity'],
'subtotal': product['subtotal']
})
+
+ if voucher:
voucher_info = voucher.apply(order_line)
discount_voucher = voucher_info['discount']['all']
subtotal -= discount_voucher
+
+ if voucher_shipping:
+ voucher_shipping_info = voucher_shipping.apply(order_line)
+ discount_voucher_shipping = voucher_shipping_info['discount']['all']
+
tax = round(subtotal * 0.11)
grand_total = subtotal + tax
total_weight = sum(x['weight'] * x['quantity'] for x in products)
@@ -133,6 +140,7 @@ class WebsiteUserCart(models.Model):
'total_purchase': total_purchase,
'total_discount': total_discount,
'discount_voucher': discount_voucher,
+ 'discount_voucher_shipping': discount_voucher_shipping,
'subtotal': subtotal,
'tax': tax,
'grand_total': round(grand_total),
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 5e7554a5..09dbb45e 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -23,6 +23,7 @@ access_website_brand_homepage,access.website.brand.homepage,model_website_brand_
access_website_categories_homepage,access.website.categories.homepage,model_website_categories_homepage,,1,1,1,1
access_website_categories_lob,access.website.categories.lob,model_website_categories_lob,,1,1,1,1
access_website_categories_management,access.website.categories.management,model_website_categories_management,,1,1,1,1
+access_website_categories_management_line,access.website.categories.management.line,model_website_categories_management_line,,1,1,1,1
access_sales_target,access.sales.target,model_sales_target,,1,1,1,1
access_purchase_outstanding,access.purchase.outstanding,model_purchase_outstanding,,1,1,1,1
access_sales_outstanding,access.sales.outstanding,model_sales_outstanding,,1,1,1,1
diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml
index 93145fea..2863af57 100644
--- a/indoteknik_custom/views/account_move.xml
+++ b/indoteknik_custom/views/account_move.xml
@@ -85,6 +85,7 @@
</field>
<field name="invoice_date_due" position="after">
<field name="new_due_date" optional="hide"/>
+ <field name="is_efaktur_exported" optional="hide"/>
<field name="invoice_day_to_due" attrs="{'invisible': [['payment_state', 'in', ('paid', 'in_payment', 'reversed')]]}" optional="hide"/>
<field name="new_invoice_day_to_due" attrs="{'invisible': [['payment_state', 'in', ('paid', 'in_payment', 'reversed')]]}" optional="hide"/>
<field name="mark_upload_efaktur" optional="hide" widget="badge"
diff --git a/indoteknik_custom/views/bill_receipt.xml b/indoteknik_custom/views/bill_receipt.xml
index 15d82e7b..02b28ddf 100644
--- a/indoteknik_custom/views/bill_receipt.xml
+++ b/indoteknik_custom/views/bill_receipt.xml
@@ -13,6 +13,7 @@
<field name="resi_tukar_faktur"/>
<field name="date_terima_tukar_faktur"/>
<field name="shipper_faktur_id"/>
+ <field name="grand_total"/>
</tree>
</field>
</record>
@@ -69,6 +70,7 @@
<field name="resi_tukar_faktur"/>
<field name="date_terima_tukar_faktur"/>
<field name="shipper_faktur_id"/>
+ <field name="grand_total"/>
</group>
</group>
<notebook>
diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml
index 567d5e8c..522be8c9 100644
--- a/indoteknik_custom/views/dunning_run.xml
+++ b/indoteknik_custom/views/dunning_run.xml
@@ -13,6 +13,7 @@
<field name="resi_tukar_faktur"/>
<field name="date_terima_tukar_faktur"/>
<field name="shipper_faktur_id"/>
+ <field name="grand_total"/>
</tree>
</field>
</record>
@@ -71,12 +72,20 @@
<field name="resi_tukar_faktur"/>
<field name="date_terima_tukar_faktur"/>
<field name="shipper_faktur_id"/>
+ <field name="grand_total"/>
</group>
</group>
<notebook>
<page id="invoice_tab" string="Invoices">
<field name="dunning_line"/>
</page>
+ <page id="others_tab" string="Others">
+ <group>
+ <field name="is_paid" readonly="1"/>
+ <field name="description" readonly="1"/>
+ <field name="comment" readonly="1"/>
+ </group>
+ </page>
</notebook>
</sheet>
<div class="oe_chatter">
diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml
index 0dfb69db..6eff0153 100644
--- a/indoteknik_custom/views/product_pricelist.xml
+++ b/indoteknik_custom/views/product_pricelist.xml
@@ -26,4 +26,18 @@
</page>
</field>
</record>
+
+ <data noupdate="1">
+ <record id="ir_cron_check_end_date" model="ir.cron">
+ <field name="name">Pricelist: Check End Date and Update Solr</field>
+ <field name="interval_number">1</field>
+ <field name="interval_type">days</field>
+ <field name="numbercall">-1</field>
+ <field name="doall" eval="False"/>
+ <field name="model_id" ref="model_product_pricelist"/>
+ <field name="code">model._check_end_date_and_update_solr()</field>
+ <field name="state">code</field>
+ <field name="priority">55</field>
+ </record>
+ </data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/product_template.xml b/indoteknik_custom/views/product_template.xml
index 520af5c8..b6155eea 100755
--- a/indoteknik_custom/views/product_template.xml
+++ b/indoteknik_custom/views/product_template.xml
@@ -8,7 +8,7 @@
<field name="arch" type="xml">
<field name="categ_id" position="after">
<field name="web_tax_id" domain="[('type_tax_use','=','sale'), ('active', '=', True)]"/>
- <field name="x_manufacture"/>
+ <field name="x_manufacture" options="{'no_create': True}"/>
<field name="x_model_product"/>
<field name="kind_of"/>
<field name="x_studio_field_tGhJR" widget="many2many_tags"/>
@@ -21,6 +21,12 @@
<field name="desc_update_solr" readonly="1" />
<field name="last_update_solr" readonly="1" />
</field>
+ <field name="public_categ_ids" position="attributes">
+ <attribute name="required">1</attribute>
+ </field>
+ <field name="public_categ_ids" position="attributes">
+ <attribute name="options">{'no_create': True}</attribute>
+ </field>
<page name="inventory" position="after">
<page string="Marketplace" name="marketplace">
<group>
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index 25b7787b..ce127fdd 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -26,20 +26,26 @@
<button name="button_unlock" position="after">
<button name="delete_line" type="object" string="Delete " states="draft"/>
</button>
+ <button name="button_unlock" position="after">
+ <button name="create_bill_dp" string="Create Bill DP" type="object" class="oe_highlight" context="{'create_bill':True}" attrs="{'invisible': ['|', ('state', 'not in', ('purchase', 'done')), ('invoice_status', 'in', ('no', 'invoiced'))]}"/>
+ </button>
<field name="date_order" position="before">
<field name="sale_order_id" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/>
<field name="sale_order"/>
<field name="is_create_uangmuka"/>
<field name="approval_status"/>
- <field name="amount_total_without_service"/>
</field>
<field name="approval_status" position="after">
- <field name="revisi_po" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/>
+ <field name="revisi_po" invisible="1"/>
+ </field>
+ <field name="incoterm_id" position="after">
+ <field name="amount_total_without_service"/>
+ <field name="delivery_amt"/>
</field>
<field name="currency_id" position="after">
<field name="summary_qty_po"/>
<field name="count_line_product"/>
- <field name="payment_term_id" required="1"/>
+ <field name="payment_term_id" readonly="1"/>
</field>
<field name="amount_total" position="after">
<field name="total_margin"/>
@@ -88,6 +94,7 @@
<field name="from_apo"/>
<field name="approval_edit_line"/>
<field name="logbook_bill_id"/>
+ <field name="bills_dp_id" readonly="1"/>
</field>
<field name="order_line" position="attributes">
@@ -229,6 +236,9 @@
<field name="product_id"/>
<field name="qty_so"/>
<field name="qty_po"/>
+ <field name="margin_item" optional="hide"/>
+ <field name="delivery_amt" optional="hide"/>
+ <field name="margin_deduct" optional="hide"/>
<field name="margin_so"/>
</tree>
</field>
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 1257ff85..f2789254 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -32,6 +32,7 @@
<field name="delivery_amt"/>
<field name="fee_third_party"/>
<field name="total_percent_margin"/>
+ <field name="type_promotion"/>
<label for="voucher_id"/>
<div class="o_row">
<field name="voucher_id" id="voucher_id" attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_id', '!=', False)]}"/>
@@ -43,6 +44,17 @@
attrs="{'invisible': ['|', ('applied_voucher_id', '=', False), ('state', 'not in', ['draft','sent'])]}"
/>
</div>
+ <label for="voucher_shipping_id"/>
+ <div class="o_row">
+ <field name="voucher_shipping_id" id="voucher_shipping_id" attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_shipping_id', '!=', False)]}"/>
+ <field name="applied_voucher_shipping_id" invisible="1" />
+ <button name="action_apply_voucher_shipping" type="object" string="Apply" confirm="Anda yakin untuk menggunakan voucher?" help="Apply the selected voucher" class="btn-link mb-1 px-0" icon="fa-plus"
+ attrs="{'invisible': ['|', '|', ('voucher_id', '=', False), ('state', 'not in', ['draft', 'sent']), ('applied_voucher_shipping_id', '!=', False)]}"
+ />
+ <button name="cancel_voucher_shipping" type="object" string="Cancel" confirm="Anda yakin untuk membatalkan penggunaan voucher?" help="Cancel applied voucher" class="btn-link mb-1 px-0" icon="fa-times"
+ attrs="{'invisible': ['|', ('applied_voucher_shipping_id', '=', False), ('state', 'not in', ['draft','sent'])]}"
+ />
+ </div>
<button name="calculate_selling_price"
string="Calculate Selling Price"
type="object"
@@ -58,6 +70,8 @@
<field name="tag_ids" position="after">
<field name="eta_date" readonly="1"/>
<field name="flash_sale"/>
+ <field name="margin_after_delivery_purchase"/>
+ <field name="percent_margin_after_delivery_purchase"/>
</field>
<field name="analytic_account_id" position="after">
<field name="customer_type" required="1"/>
@@ -112,6 +126,7 @@
"/>
<field name="purchase_tax_id" attrs="{'readonly': [('parent.approval_status', '!=', False)]}" domain="[('type_tax_use','=','purchase')]"/>
<field name="item_percent_margin"/>
+ <field name="item_margin" optional="hide"/>
<field name="note" optional="hide"/>
<field name="note_procurement" optional="hide"/>
<field name="vendor_subtotal" optional="hide"/>
@@ -138,6 +153,11 @@
<field class="mb-0" name="amount_voucher_disc" string="Voucher" readonly="1"/>
<div class="text-right mb-2"><small>*Hanya informasi</small></div>
</div>
+ <label for="amount_voucher_shipping_disc" string="Voucher Shipping" />
+ <div>
+ <field class="mb-0" name="amount_voucher_shipping_disc" string="Voucher Shipping" readonly="1"/>
+ <div class="text-right mb-2"><small>*Hanya informasi</small></div>
+ </div>
<field name="total_margin"/>
<field name="total_percent_margin"/>
</field>
diff --git a/indoteknik_custom/views/website_categories_management.xml b/indoteknik_custom/views/website_categories_management.xml
index 648814e2..6ad85944 100644
--- a/indoteknik_custom/views/website_categories_management.xml
+++ b/indoteknik_custom/views/website_categories_management.xml
@@ -29,16 +29,16 @@
<group>
<field name="sequence"/>
<field name="category_id"/>
- <field name="category_id2" widget="many2many_tags"/>
<field name="status"/>
</group>
</group>
<notebook>
- <page string="Detail category">
- <field name="category_id2">
+ <page string="Category Level 2 Lines">
+ <field name="line_ids">
<tree editable="bottom">
- <field name="name"/>
- <field name="child_frontend_id2" widget="many2many_tags"/>
+ <field name="category_id2" domain="[('parent_frontend_id', '=', parent.category_id)]"/> <!-- Category Level 2 -->
+ <field name="category_id3_ids" widget="many2many_tags"/> <!-- Category Level 3 -->
+ <field name="sequence" widget="handle" />
</tree>
</field>
</page>
@@ -48,13 +48,13 @@
</field>
</record>
-<!-- <record id="ir_actions_server_website_categories_management_sync_to_solr" model="ir.actions.server">-->
-<!-- <field name="name">Sync to solr</field>-->
-<!-- <field name="model_id" ref="indoteknik_custom.model_website_categories_management"/>-->
-<!-- <field name="binding_model_id" ref="indoteknik_custom.model_website_categories_management"/>-->
-<!-- <field name="state">code</field>-->
-<!-- <field name="code">model.action_sync_to_solr()</field>-->
-<!-- </record>-->
+ <record id="ir_actions_server_website_categories_management_sync_to_solr" model="ir.actions.server">
+ <field name="name">Sync to solr</field>
+ <field name="model_id" ref="indoteknik_custom.model_website_categories_management"/>
+ <field name="binding_model_id" ref="indoteknik_custom.model_website_categories_management"/>
+ <field name="state">code</field>
+ <field name="code">model.action_sync_to_solr()</field>
+ </record>
<menuitem
id="website_categories_management"