summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortrisusilo48 <tri.susilo@altama.co.id>2024-10-02 09:44:39 +0700
committertrisusilo48 <tri.susilo@altama.co.id>2024-10-02 09:44:39 +0700
commit2739d3040a69228192096ee16373610149a2fb47 (patch)
tree545aa797d7407e8f22250dff15648495b891928f
parent4d3d219b5f1002822a16067a28261fd59b170ff8 (diff)
parent7d3780ede67579b5891218efc370dc82eef510a1 (diff)
Merge branch 'production' of https://bitbucket.org/altafixco/indoteknik-addons into production
-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.py73
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py20
-rw-r--r--indoteknik_api/controllers/api_v1/user.py280
-rw-r--r--indoteknik_api/models/res_users.py3
-rwxr-xr-xindoteknik_custom/__manifest__.py1
-rwxr-xr-xindoteknik_custom/models/__init__.py3
-rw-r--r--indoteknik_custom/models/account_move.py2
-rw-r--r--indoteknik_custom/models/account_move_due_extension.py2
-rw-r--r--indoteknik_custom/models/account_tax.py22
-rw-r--r--indoteknik_custom/models/approval_unreserve.py146
-rw-r--r--indoteknik_custom/models/automatic_purchase.py8
-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.py9
-rwxr-xr-xindoteknik_custom/models/product_template.py16
-rw-r--r--indoteknik_custom/models/promotion/promotion_program_line.py23
-rwxr-xr-xindoteknik_custom/models/purchase_order.py71
-rw-r--r--indoteknik_custom/models/purchase_order_sales_match.py12
-rwxr-xr-xindoteknik_custom/models/purchase_pricelist.py8
-rw-r--r--indoteknik_custom/models/res_partner.py97
-rwxr-xr-xindoteknik_custom/models/res_users.py19
-rwxr-xr-xindoteknik_custom/models/sale_order.py274
-rw-r--r--indoteknik_custom/models/sale_order_line.py4
-rw-r--r--indoteknik_custom/models/solr/__init__.py3
-rw-r--r--indoteknik_custom/models/solr/product_template.py4
-rw-r--r--indoteknik_custom/models/solr/promotion_program_line.py5
-rw-r--r--indoteknik_custom/models/solr/website_categories_management.py114
-rw-r--r--indoteknik_custom/models/stock_move.py44
-rw-r--r--indoteknik_custom/models/stock_picking.py22
-rw-r--r--indoteknik_custom/models/user_company_request.py26
-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.py25
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv4
-rw-r--r--indoteknik_custom/views/account_move.xml1
-rw-r--r--indoteknik_custom/views/account_move_views.xml248
-rw-r--r--indoteknik_custom/views/approval_unreserve.xml81
-rw-r--r--indoteknik_custom/views/bill_receipt.xml2
-rw-r--r--indoteknik_custom/views/dunning_run.xml2
-rw-r--r--indoteknik_custom/views/ir_sequence.xml10
-rwxr-xr-xindoteknik_custom/views/product_template.xml2
-rw-r--r--indoteknik_custom/views/promotion/promotion_program_line.xml5
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml14
-rw-r--r--indoteknik_custom/views/res_users.xml197
-rwxr-xr-xindoteknik_custom/views/sale_order.xml23
-rw-r--r--indoteknik_custom/views/user_company_request.xml2
-rw-r--r--indoteknik_custom/views/website_categories_management.xml24
-rwxr-xr-xindoteknik_custom/views/website_user_cart.xml7
50 files changed, 1840 insertions, 212 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 69a2f861..369f2125 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -66,11 +66,13 @@ class Partner(controller.Controller):
'name': ['required'],
'email': ['required'],
'mobile': ['required'],
+ 'phone': [''],
'street': ['required'],
'city_id': ['required', 'number', 'alias:kota_id'],
'district_id': ['number', 'alias:kecamatan_id'],
'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'],
'zip': ['required'],
+ 'alamat_lengkap_text': []
})
if not params['valid']:
@@ -95,6 +97,7 @@ class Partner(controller.Controller):
'name': ['required'],
'email': ['required'],
'mobile': ['required'],
+ 'phone': [''],
'street': ['required'],
'city_id': ['required', 'number', 'alias:kota_id'],
'district_id': ['number', 'alias:kecamatan_id'],
@@ -115,23 +118,55 @@ class Partner(controller.Controller):
@controller.Controller.must_authorized()
def write_partner_by_id(self, **kw):
params = self.get_request_params(kw, {
- 'id': ['required', 'number'],
+ 'id': ['', 'number'],
'name': [],
'company_type_id': ['number'],
'industry_id': ['number'],
'tax_name': ['alias:nama_wajib_pajak'],
'npwp': [],
+ 'alamat_lengkap_text': [],
+ 'street': [],
+ })
+
+ # Mengambil id_user dari request
+ id_user = self.get_request_params(kw, {
+ 'id_user': ['number']
+ })
+
+ # Mengambil parameter user dari request
+ params_user = self.get_request_params(kw, {
+ 'company_type_id': ['number'],
+ 'industry_id': ['number'],
+ 'tax_name': ['alias:nama_wajib_pajak'],
+ 'npwp': [],
+ 'alamat_lengkap_text': [],
})
+ # Cek validitas parameter
if not params['valid']:
return self.response(code=400, description=params)
-
+
+ # Mencari partner dan user berdasarkan ID
partner = request.env[self._name].search([('id', '=', params['value']['id'])], limit=1)
+ user = request.env[self._name].search([('id', '=', id_user['value']['id_user'])], limit=1)
+
if not partner:
- return self.response(code=404, description='User not found')
-
- partner.write(params['value'])
+ return self.response(code=404, description='Partner not found')
+
+ # Filter parameter yang memiliki nilai saja untuk partner
+ params_filtered = {k: v for k, v in params['value'].items() if v}
+
+ # Filter parameter yang memiliki nilai saja untuk user
+ params_user_filtered = {k: v for k, v in params_user['value'].items() if v}
+ # Update partner dan user hanya dengan parameter yang memiliki nilai
+ if params_filtered:
+ partner.write(params_filtered)
+
+ if params_user_filtered:
+ user.write(params_user_filtered)
+
+ # Return response dengan ID partner yang di-update
return self.response({
'id': partner.id
})
@@ -164,4 +199,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 1b7c1af4..e7664c79 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -388,6 +388,9 @@ class SaleOrder(controller.Controller):
if not params['valid']:
return self.response(code=400, description=params)
+ # Fetch partner details
+ sales_partner = request.env['res.partner'].browse(params['value']['partner_id'])
+
parameters = {
'warehouse_id': 8,
'carrier_id': 1,
@@ -407,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',
@@ -415,9 +418,11 @@ class SaleOrder(controller.Controller):
'delivery_service_type': params['value']['delivery_service_type'],
'flash_sale': params['value']['flash_sale'],
'note_website': params['value']['note_website'],
- 'customer_type': 'nonpkp',
- 'npwp': '0',
- 'user_id': 3222 # User ID: Nadia Rauhadatul Firdaus
+ 'customer_type': sales_partner.customer_type if sales_partner else 'nonpkp', # Get Customer Type from partner
+ 'npwp': sales_partner.npwp or '0', # Get NPWP from partner
+ 'sppkp': sales_partner.sppkp, # Get SPPKP from partner
+ 'email': sales_partner.email, # Get Email from partner
+ 'user_id': 3222 # User ID: Nadia Rauhadatul Firdaus
}
sales_partner = request.env['res.partner'].browse(parameters['partner_id'])
@@ -460,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'
diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py
index 10508149..529873c0 100644
--- a/indoteknik_api/controllers/api_v1/user.py
+++ b/indoteknik_api/controllers/api_v1/user.py
@@ -9,6 +9,8 @@ import string
import requests
import json
from difflib import SequenceMatcher
+import mimetypes
+import base64
class User(controller.Controller):
@@ -105,12 +107,28 @@ class User(controller.Controller):
name = kw.get('name')
email = kw.get('email')
password = kw.get('password')
- if not name or not email or not password:
- return self.response(code=400, description='email, name and password is required')
-
- company = kw.get('company', False)
phone = kw.get('phone')
+ # Form Data
+ npwp = kw.get('npwp')
+ npwp_document = kw.get('npwp_document', False)
+ npwp_filename = kw.get('npwp_filename', 'npwp_document')
+ sppkp = kw.get('sppkp', False)
+ sppkp_document = kw.get('sppkp_document', False)
+ sppkp_filename = kw.get('sppkp_filename', 'sppkp_document')
+ email_partner = kw.get('email_partner')
+ business_name = kw.get('business_name', False)
+ industry_id = int(kw.get('industry_id', '0') or 0)
+ company_type_id = int(kw.get('company_type_id', '0') or 0)
+ alamat_wajib_pajak = kw.get('alamat_wajib_pajak', False)
+ alamat_bisnis = kw.get('alamat_bisnis', False)
+ nama_wajib_pajak = kw.get('nama_wajib_pajak', False)
+ is_pkp = kw.get('is_pkp')
+ type_acc = kw.get('type_acc', 'individu') or 'individu'
+
+ if not name or not email or not password:
+ return self.response(code=400, description='email, name, and password are required')
+
response = {
'register': False,
'reason': None
@@ -126,10 +144,11 @@ class User(controller.Controller):
return self.response(response)
+ # Create user data
user_data = {
'name': name,
'login': email,
- 'phone': phone,
+ 'mobile': phone,
'password': password,
'active': False,
'sel_groups_1_9_10': 9
@@ -137,27 +156,97 @@ class User(controller.Controller):
user = request.env['res.users'].create(user_data)
user.partner_id.email = email
-
- if company:
- parameter = [
- ('company_type', '=', 'company'),
- ('name', 'ilike', company)
- ]
- match_company = request.env['res.partner'].search(parameter, limit=1)
- match_ratio = 0
- if match_company:
- match_ratio = SequenceMatcher(None, match_company.name, company).ratio()
- if match_ratio > 0.8:
+ user.partner_id.mobile = phone
+
+ if type_acc == 'business' and business_name:
+ # Eksekusi query SQL menggunakan Levenshtein distance
+ query = """
+ SELECT name, levenshtein(name::text, %s) AS distance
+ FROM res_partner
+ WHERE levenshtein(name::text, %s) < 3
+ ORDER BY distance ASC
+ """
+ params = (business_name, business_name)
+ request.env.cr.execute(query, params)
+ result = request.env.cr.fetchone()
+
+ if result:
+ match_company_name = result[0]
+ match_company_id = result[2]
+
+ # Create a user company request
request.env['user.company.request'].create({
'user_id': user.partner_id.id,
- 'user_company_id': match_company.id,
- 'user_input': company
+ 'user_company_id': match_company_id,
+ 'user_input': business_name
})
else:
- new_company = request.env['res.partner'].create({
- 'name': company
+ if not nama_wajib_pajak and is_pkp == 'false':
+ nama_wajib_pajak = business_name
+
+ if not alamat_wajib_pajak and is_pkp == 'false':
+ alamat_wajib_pajak = alamat_bisnis
+
+ new_company_data = {
+ 'name': business_name if business_name else business_name,
+ 'company_type_id': company_type_id if company_type_id else False,
+ 'industry_id': industry_id,
+ 'customer_type': 'pkp' if is_pkp == 'true' else 'nonpkp',
+ 'npwp': npwp if is_pkp == 'true' else (npwp if npwp else '00.000.000.0-000.000'),
+ 'sppkp': sppkp if is_pkp == 'true' else (sppkp if sppkp else '-'),
+ 'nama_wajib_pajak': nama_wajib_pajak,
+ 'alamat_lengkap_text': alamat_wajib_pajak,
+ 'email': email_partner,
+ 'street': alamat_bisnis,
+ 'company_type': 'company',
+ 'user_id': 3222,
+ 'property_account_receivable_id': 395,
+ 'property_account_payable_id': 438,
+ 'active': False,
+ }
+ new_company = request.env['res.partner'].create(new_company_data)
+ request.env['user.company.request'].create({
+ 'user_id': user.partner_id.id,
+ 'user_company_id': new_company.id,
+ 'user_input': business_name
})
- user.parent_id = new_company.id
+
+ if npwp_document:
+ npwp_mimetype, _ = mimetypes.guess_type(npwp_filename)
+ npwp_mimetype = npwp_mimetype or 'application/octet-stream'
+ pdf_data = base64.b64decode(npwp_document)
+ npwp_attachment = request.env['ir.attachment'].create({
+ 'name': 'NPWP Document',
+ 'type': 'binary',
+ 'datas': base64.b64encode(pdf_data),
+ 'res_model': 'res.partner',
+ 'res_id': new_company.id,
+ 'mimetype': npwp_mimetype
+ })
+ new_company.message_post(body="NPWP Uploaded", attachment_ids=[npwp_attachment.id])
+
+ if sppkp_document:
+ sppkp_mimetype, _ = mimetypes.guess_type(sppkp_filename)
+ sppkp_mimetype = sppkp_mimetype or 'application/octet-stream'
+ pdf_data = base64.b64decode(sppkp_document)
+ sppkp_attachment = request.env['ir.attachment'].create({
+ 'name': 'SPPKP Document',
+ 'type': 'binary',
+ 'datas': base64.b64encode(pdf_data),
+ 'res_model': 'res.partner',
+ 'res_id': new_company.id,
+ 'mimetype': sppkp_mimetype
+ })
+ new_company.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id])
+
+ if type_acc == 'individu':
+ user.partner_id.customer_type = 'nonpkp'
+ user.partner_id.npwp = '0.000.000.0-000.000'
+ user.partner_id.sppkp = '-'
+ user.partner_id.nama_wajib_pajak = name
+ user.partner_id.user_id = 3222
+ user.partner_id.property_account_receivable_id= 395
+ user.partner_id.property_account_payable_id = 438
user.send_activation_mail()
@@ -240,6 +329,14 @@ class User(controller.Controller):
'activation': True,
'user': self.response_with_token(user)
})
+ parameter = [
+ ('user_id', '=', user.partner_id.id)
+ ]
+ company_request = request.env['user.company.request'].search(parameter, limit=1)
+ if company_request:
+ user.parent_name = company_request.user_input
+ if company_request.is_approve == False:
+ user.send_company_request_mail()
return self.response(response)
@http.route(prefix + 'user/forgot-password', auth='public', methods=['POST'], csrf=False)
@@ -315,3 +412,144 @@ class User(controller.Controller):
x) for x in partners]
return self.response(address)
+
+ @http.route(prefix + 'user/<id>/switch', auth='public', methods=['PUT', 'OPTIONS'], csrf=False)
+ @controller.Controller.must_authorized()
+ def switch_account(self, **kw):
+ id = kw.get('id')
+ user = request.env['res.users'].search([('id', '=', id)], limit=1)
+
+ response = {
+ 'switch': False,
+ 'reason': None
+ }
+
+ # Form Data
+ npwp = kw.get('npwp')
+ npwp_document = kw.get('npwp_document', False)
+ npwp_filename = kw.get('npwp_filename', 'npwp_document')
+ sppkp = kw.get('sppkp', False)
+ sppkp_document = kw.get('sppkp_document', False)
+ sppkp_filename = kw.get('sppkp_filename', 'sppkp_document')
+ email_partner = kw.get('email_partner')
+ business_name = kw.get('business_name', False)
+ industry_id = int(kw.get('industry_id', '0') or 0)
+ company_type_id = int(kw.get('company_type_id', '0') or 0)
+ alamat_wajib_pajak = kw.get('alamat_wajib_pajak', False)
+ alamat_bisnis = kw.get('alamat_bisnis', False)
+ nama_wajib_pajak = kw.get('nama_wajib_pajak', False)
+ is_pkp = kw.get('is_pkp')
+ type_acc = kw.get('type_acc', False)
+
+
+ response = {
+ 'switch': False,
+ 'reason': None
+ }
+
+ if type_acc == 'business':
+ parameter = [
+ ('company_type', '=', 'company'),
+ ('name', 'ilike', business_name)
+ ]
+ match_company = request.env['res.partner'].search(parameter, limit=1)
+ match_ratio = 0
+ if match_company:
+ match_ratio = SequenceMatcher(None, match_company.name, business_name).ratio()
+ if match_ratio > 0.8:
+ # Create a user company request
+ request.env['user.company.request'].create({
+ 'user_id': user.partner_id.id,
+ 'user_company_id': match_company.id,
+ 'user_input': business_name
+ })
+ else:
+ if not nama_wajib_pajak and is_pkp == 'false':
+ nama_wajib_pajak = business_name
+
+ if not alamat_wajib_pajak and is_pkp == 'false':
+ alamat_wajib_pajak = alamat_bisnis
+
+ new_company_data = {
+ 'name': business_name if business_name else business_name,
+ 'company_type_id': company_type_id if company_type_id else False,
+ 'industry_id': industry_id,
+ 'customer_type': 'pkp' if is_pkp == 'true' else 'nonpkp',
+ 'npwp': npwp if is_pkp == 'true' else (npwp if npwp else '00.000.000.0-000.000'),
+ 'sppkp': sppkp if is_pkp == 'true' else (sppkp if sppkp else '-'),
+ 'nama_wajib_pajak': nama_wajib_pajak,
+ 'alamat_lengkap_text': alamat_wajib_pajak,
+ 'email': email_partner,
+ 'street': alamat_bisnis,
+ 'company_type': 'company',
+ 'user_id': 3222,
+ 'property_account_receivable_id': 395,
+ 'property_account_payable_id': 438,
+ 'active': False,
+ }
+ new_company = request.env['res.partner'].create(new_company_data)
+ request.env['user.company.request'].create({
+ 'user_id': user.partner_id.id,
+ 'user_company_id': new_company.id,
+ 'user_input': business_name
+ })
+ # user.partner_id.parent_id = new_company.id
+ # user.partner_id.customer_type = new_company.customer_type
+ # user.partner_id.npwp = new_company.npwp
+ # user.partner_id.sppkp = new_company.sppkp
+ # user.partner_id.nama_wajib_pajak = new_company.nama_wajib_pajak
+ # user.partner_id.alamat_lengkap_text = new_company.alamat_lengkap_text
+
+ if npwp_document:
+ npwp_mimetype, _ = mimetypes.guess_type(npwp_filename)
+ npwp_mimetype = npwp_mimetype or 'application/octet-stream'
+ pdf_data = base64.b64decode(npwp_document)
+ npwp_attachment = request.env['ir.attachment'].create({
+ 'name': 'NPWP Document',
+ 'type': 'binary',
+ 'datas': base64.b64encode(pdf_data),
+ 'res_model': 'res.partner',
+ 'res_id': new_company.id,
+ 'mimetype': npwp_mimetype
+ })
+ new_company.message_post(body="NPWP Uploaded", attachment_ids=[npwp_attachment.id])
+
+ if sppkp_document:
+ sppkp_mimetype, _ = mimetypes.guess_type(sppkp_filename)
+ sppkp_mimetype = sppkp_mimetype or 'application/octet-stream'
+ pdf_data = base64.b64decode(sppkp_document)
+ sppkp_attachment = request.env['ir.attachment'].create({
+ 'name': 'SPPKP Document',
+ 'type': 'binary',
+ 'datas': base64.b64encode(pdf_data),
+ 'res_model': 'res.partner',
+ 'res_id': new_company.id,
+ 'mimetype': sppkp_mimetype
+ })
+ new_company.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id])
+
+ response['switch'] = 'Pending'
+ return self.response(response)
+
+ @http.route(prefix + 'user/<id>/switch_progres', auth='public', methods=['GET', 'OPTIONS'], csrf=False)
+ # @controller.Controller.must_authorized()
+ def switch_account_progres(self, **kw):
+ id = int(kw.get('id'))
+ user = request.env['res.users'].search([('id', '=', id)], limit=1)
+ response = {
+ 'status': ''
+ }
+ parameter = [
+ ('user_id', '=', id)
+ ]
+ new_company_request = request.env['user.company.request'].search(parameter, limit=1)
+ is_approve = ''
+ if new_company_request:
+ # Mengambil nilai is_approve
+ if new_company_request.is_approve == False:
+ response['status'] = 'pending'
+ else:
+ response['status'] = new_company_request.is_approve
+ else:
+ response['status'] = 'unknown'
+ return self.response(response) \ No newline at end of file
diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py
index d5dff876..b2e8acfe 100644
--- a/indoteknik_api/models/res_users.py
+++ b/indoteknik_api/models/res_users.py
@@ -43,6 +43,7 @@ class ResUsers(models.Model):
'type': user.type or '',
'name': user.name or '',
'mobile': user.mobile or '',
+ 'phone': user.phone or '',
'email': user.email or '',
'street': user.street or '',
'street2': user.street2 or '',
@@ -55,6 +56,8 @@ class ResUsers(models.Model):
'tax_name': user.nama_wajib_pajak or '',
'npwp': user.npwp or '',
'rajaongkir_city_id': user.kecamatan_id.rajaongkir_id or 0,
+ 'alamat_wajib_pajak': user.alamat_lengkap_text or None,
+ 'alamat_bisnis': user.street or None,
}
if user.kota_id:
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 8d8e8cec..e1a67592 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -142,6 +142,7 @@
'views/approval_date_doc.xml',
'views/partner_payment_term.xml',
'views/vendor_payment_term.xml',
+ 'views/approval_unreserve.xml',
'report/report.xml',
'report/report_banner_banner.xml',
'report/report_banner_banner2.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index e9ce587c..3d700ce0 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
@@ -125,3 +126,5 @@ from . import sale_order_multi_uangmuka_penjualan
from . import shipment_group
from . import sales_order_reject
from . import approval_date_doc
+from . import account_tax
+from . import approval_unreserve
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/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py
index 0399c6a2..23f8888c 100644
--- a/indoteknik_custom/models/account_move_due_extension.py
+++ b/indoteknik_custom/models/account_move_due_extension.py
@@ -96,6 +96,8 @@ class DueExtension(models.Model):
sales.action_confirm()
self.order_id.due_id = self.id
+ template = self.env.ref('indoteknik_custom.mail_template_due_extension_approve')
+ template.send_mail(self.id, force_send=True)
def generate_due_line(self):
partners = self.partner_id.get_child_ids()
diff --git a/indoteknik_custom/models/account_tax.py b/indoteknik_custom/models/account_tax.py
new file mode 100644
index 00000000..e39546f2
--- /dev/null
+++ b/indoteknik_custom/models/account_tax.py
@@ -0,0 +1,22 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+
+class AccountTax(models.Model):
+ _inherit = 'account.tax'
+
+ @api.model
+ def create(self, vals):
+ group_id = self.env.ref('indoteknik_custom.group_role_it').id
+ users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
+ if self.env.user.id not in users_in_group.mapped('id'):
+ raise UserError('Hanya IT yang bisa membuat tax')
+ result = super(AccountTax, self).create(vals)
+ return result
+
+ def write(self, values):
+ group_id = self.env.ref('indoteknik_custom.group_role_it').id
+ users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
+ if self.env.user.id not in users_in_group.mapped('id'):
+ raise UserError('Hanya IT yang bisa mengedit tax')
+ result = super(AccountTax, self).write(values)
+ return result \ No newline at end of file
diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py
new file mode 100644
index 00000000..88409c37
--- /dev/null
+++ b/indoteknik_custom/models/approval_unreserve.py
@@ -0,0 +1,146 @@
+from odoo import models, api, fields
+from odoo.exceptions import AccessError, UserError, ValidationError
+from datetime import timedelta, date
+import logging
+
+_logger = logging.getLogger(__name__)
+
+class ApprovalUnreserve(models.Model):
+ _name = "approval.unreserve"
+ _description = "Approval Unreserve"
+ _inherit = ['mail.thread']
+ _rec_name = 'number'
+
+ number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True, default='New')
+ approval_line = fields.One2many('approval.unreserve.line', 'approval_id', string='Approval Unreserve Lines', auto_join=True)
+ state = fields.Selection([
+ ('draft', 'Draft'),
+ ('waiting_approval', 'Waiting for Approval'),
+ ('approved', 'Approved'),
+ ('rejected', 'Rejected')
+ ], string="Status", default='draft', tracking=True)
+ request_date = fields.Date(string="Request Date", default=fields.Date.today, tracking=True)
+ approved_by = fields.Many2one('res.users', string="Approved By", readonly=True, tracking=True)
+ picking_id = fields.Many2one('stock.picking', string="Picking", tracking=True)
+ user_id = fields.Many2one('res.users', string="User", readonly=True, tracking=True)
+ rejection_reason = fields.Text(string="Rejection Reason", tracking=True)
+ reason = fields.Text(string="Reason", tracking=True)
+
+ @api.constrains('picking_id')
+ def create_move_id_line(self):
+ if not self.picking_id:
+ raise ValidationError("Picking is required")
+
+ stock_move = self.env['stock.move'].search([('picking_id', '=', self.picking_id.id), ('state', '=', 'assigned')])
+
+ if not stock_move:
+ raise ValidationError("Picking is not found")
+
+ for move in stock_move:
+ self.approval_line.create({
+ 'approval_id': self.id,
+ 'move_id': move.id
+ })
+
+ self.user_id = self.picking_id.sale_id.user_id.id
+
+ @api.model
+ def create(self, vals):
+ if vals.get('number', 'New') == 'New':
+ vals['number'] = self.env['ir.sequence'].next_by_code('approval.unreserve') or 'New'
+ return super(ApprovalUnreserve, self).create(vals)
+
+ def action_submit_for_approval(self):
+ self._check_product_and_qty()
+ self.write({'state': 'waiting_approval'})
+
+
+ def _check_product_and_qty(self):
+ stock_move = self.env['stock.move']
+ for line in self.approval_line:
+ if line.dest_picking_id:
+ move = stock_move.search([
+ ('picking_id', '=', line.dest_picking_id.id),
+ ('product_id', '=', line.product_id.id),
+ ('state', 'not in', ['done', 'cancel'])
+ ])
+
+ if not move:
+ raise UserError("Product tidak ada di destination picking")
+
+ qty_unreserve = line.unreserve_qty + move.forecast_availability
+
+ if move.product_uom_qty < qty_unreserve:
+ raise UserError("Quantity yang di unreserve melebihi quantity yang ada")
+
+ def action_approve(self):
+ if self.env.user.id != self.user_id.id:
+ raise UserError("Hanya Sales nya yang bisa approve.")
+
+ if self.state != 'waiting_approval':
+ raise UserError("Approval can only be done in 'Waiting for Approval' state")
+
+ self.write({
+ 'state': 'approved',
+ 'approved_by': self.env.user.id
+ })
+ # Trigger the unreserve function
+ self._trigger_unreserve()
+
+ def action_reject(self, reason):
+ if self.env.user.id != self.user_id.id:
+ raise UserError("Hanya Sales nya yang bisa reject.")
+
+ if self.state != 'waiting_approval':
+ raise UserError("Rejection can only be done in 'Waiting for Approval' state")
+ self.write({
+ 'state': 'rejected',
+ 'rejection_reason': reason
+ })
+
+ def _trigger_unreserve(self):
+ stock_move_obj = self.env['stock.move']
+
+ for line in self.approval_line:
+ move = stock_move_obj.browse(line.move_id.id)
+ move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty)
+
+ original_sale_id = move.picking_id.sale_id
+
+ product_name = line.product_id.display_name
+ unreserved_qty = line.unreserve_qty
+
+ if line.dest_picking_id:
+ dest_sale_id = line.dest_picking_id.sale_id
+ line.dest_picking_id.action_assign()
+
+ if original_sale_id:
+ message = (
+ f"Barang {product_name} sebanyak {unreserved_qty} dipindahkan ke SO {dest_sale_id.name}"
+ if dest_sale_id else
+ f"Barang {product_name} sebanyak {unreserved_qty} dipindahkan ke picking tujuan."
+ )
+ original_sale_id.message_post(body=message)
+ else:
+ if original_sale_id:
+ message = f"Barang {product_name} sebanyak {unreserved_qty} ter unreserve."
+ original_sale_id.message_post(body=message)
+
+
+class ApprovalUnreserveLine(models.Model):
+ _name = 'approval.unreserve.line'
+ _description = 'Approval Unreserve Line'
+ _order = 'approval_id, id'
+
+ approval_id = fields.Many2one('approval.unreserve', string='Approval Reference', required=True, ondelete='cascade', index=True, copy=False)
+ move_id = fields.Many2one('stock.move', string="Stock Move", required=True)
+ product_id = fields.Many2one('product.product', string="Product", related='move_id.product_id', readonly=True)
+ sales_id = fields.Many2one('res.users', string="Sales", readonly=True, tracking=True)
+ unreserve_qty = fields.Float(string="Quantity to Unreserve")
+ dest_picking_id = fields.Many2one('stock.picking', string="Destination Picking", tracking=True)
+
+
+ @api.onchange('dest_picking_id')
+ def onchange_dest_picking_id(self):
+ if self.dest_picking_id:
+ self.sales_id = self.dest_picking_id.sale_id.user_id.id
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index 1d1322fa..548115e6 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -262,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)
@@ -293,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,
@@ -305,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 90159cd0..c167aab7 100644
--- a/indoteknik_custom/models/dunning_run.py
+++ b/indoteknik_custom/models/dunning_run.py
@@ -30,7 +30,14 @@ class DunningRun(models.Model):
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_template.py b/indoteknik_custom/models/product_template.py
index e6778758..031d1b5b 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -338,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)
@@ -388,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/promotion/promotion_program_line.py b/indoteknik_custom/models/promotion/promotion_program_line.py
index a57f1f2c..7a15ad11 100644
--- a/indoteknik_custom/models/promotion/promotion_program_line.py
+++ b/indoteknik_custom/models/promotion/promotion_program_line.py
@@ -34,6 +34,29 @@ class PromotionProgramLine(models.Model):
active = fields.Boolean(string="Active", default=True)
solr_flag = fields.Integer(string="Solr Flag", default=1)
description = fields.Char('Description')
+ price_tier_1 = fields.Float('Price Tier 1')
+ price_tier_2 = fields.Float('Price Tier 2')
+ price_tier_3 = fields.Float('Price Tier 3')
+ price_tier_4 = fields.Float('Price Tier 4')
+ price_tier_5 = fields.Float('Price Tier 5')
+
+ def get_price_tier(self, product_id, qty):
+ product = self.env['product.product'].browse(product_id.id)
+
+ tiers = ['1_v2', '2_v2', '3_v2', '4_v2', '5_v2']
+
+ for index, tier in enumerate(tiers, start=1):
+
+ price_tier_data = product._get_pricelist_tier(tier)
+
+ price_field = f'price_tier_{index}'
+
+ if price_field in self._fields:
+ price = price_tier_data.get(f'price_tier{tier}', 0) * qty
+ self[price_field] = price
+
+ if index == 1:
+ self.price = price
def get_active_promotions(self, product_id):
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 8ec904a9..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([
@@ -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/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py
index 68fb796e..e5b35d7f 100755
--- a/indoteknik_custom/models/purchase_pricelist.py
+++ b/indoteknik_custom/models/purchase_pricelist.py
@@ -23,6 +23,13 @@ class PurchasePricelist(models.Model):
brand_id = fields.Many2one('x_manufactures', string='Brand')
count_brand_vendor = fields.Integer(string='Count Brand Vendor')
is_winner = fields.Boolean(string='Winner', default=False, help='Pemenang yang direkomendasikan oleh Merchandise')
+
+ def sync_pricelist_tier(self):
+ for rec in self:
+ promotion_product = self.env['promotion.product'].search([('product_id', '=', rec.product_id.id)])
+
+ for promotion in promotion_product:
+ promotion.program_line_id.get_price_tier(promotion.product_id, promotion.qty)
@api.depends('product_id', 'vendor_id')
def _compute_name(self):
@@ -111,5 +118,6 @@ class PurchasePricelist(models.Model):
tier_prod_pricelist = self.env['product.pricelist.item'].search(product_domain + [('pricelist_id', '=', tier_pricelist.id)], limit=1)
tier_prod_pricelist.price_discount = tier_perc
+ rec.sync_pricelist_tier()
rec.product_id.product_tmpl_id._create_solr_queue('_sync_price_to_solr')
\ No newline at end of file
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index ac126337..2846c14b 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -18,7 +18,8 @@ class ResPartner(models.Model):
('pkp', 'PKP'),
('nonpkp', 'Non PKP')
])
- sppkp = fields.Char(string="SPPKP")
+ sppkp = fields.Char(string="SPPKP", tracking=3)
+ npwp = fields.Char(string="NPWP", tracking=3)
counter = fields.Integer(string="Counter", default=0)
leadtime = fields.Integer(string="Leadtime", default=0)
digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak")
@@ -58,20 +59,98 @@ class ResPartner(models.Model):
default=_default_payment_term
)
+ @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id",
+ "kecamatan_id")
+ def _alamat_lengkap_text(self):
+ for partner in self:
+ if partner.company_type == 'person' and not partner.parent_id:
+ partner.alamat_lengkap_text = partner.street
+ # if partner.company_type == 'person' and partner.parent_id:
+ # partner.alamat_lengkap_text = partner.parent_id.alamat_lengkap_text
+
+
+ alamat_lengkap_text = fields.Text(string="Alamat Lengkap", required=False , tracking=3)
+
def write(self, vals):
res = super(ResPartner, self).write(vals)
+ #
+ # # if 'property_payment_term_id' in vals:
+ # # if not self.env.user.is_accounting and vals['property_payment_term_id'] != 26:
+ # # raise UserError('Hanya Finance Accounting yang dapat merubah payment term')
+ #
+ # # group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id
+ # # users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
+ # # if self.env.user.id not in users_in_group.mapped('id'):
+ # # raise UserError('You name it')
+ #
+ return res
- # if 'property_payment_term_id' in vals:
- # if not self.env.user.is_accounting and vals['property_payment_term_id'] != 26:
- # raise UserError('Hanya Finance Accounting yang dapat merubah payment term')
-
- # group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id
- # users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
- # if self.env.user.id not in users_in_group.mapped('id'):
- # raise UserError('You name it')
+ def write(self, vals):
+ # Fungsi rekursif untuk meng-update semua child, termasuk child dari child
+ def update_children_recursively(partner, vals_for_child):
+ # Lakukan update pada partner saat ini hanya dengan field yang diizinkan
+ partner.write(vals_for_child)
+
+ # Untuk setiap child dari partner ini, update juga child-nya
+ for child in partner.child_ids:
+ update_children_recursively(child, vals_for_child)
+
+ # Jika self tidak memiliki parent_id, artinya self adalah parent
+ if not self.parent_id:
+ # Ambil semua child dari parent ini
+ children = self.child_ids
+
+ # Perbarui vals dengan nilai dari parent jika tidak ada dalam vals
+ vals['customer_type'] = vals.get('customer_type', self.customer_type)
+ vals['nama_wajib_pajak'] = vals.get('nama_wajib_pajak', self.nama_wajib_pajak)
+ vals['npwp'] = vals.get('npwp', self.npwp)
+ vals['sppkp'] = vals.get('sppkp', self.sppkp)
+ vals['alamat_lengkap_text'] = vals.get('alamat_lengkap_text', self.alamat_lengkap_text)
+ vals['industry_id'] = vals.get('industry_id', self.industry_id.id if self.industry_id else None)
+ vals['company_type_id'] = vals.get('company_type_id',
+ self.company_type_id.id if self.company_type_id else None)
+
+ # Simpan hanya field yang perlu di-update pada child
+ vals_for_child = {
+ 'customer_type': vals.get('customer_type'),
+ 'nama_wajib_pajak': vals.get('nama_wajib_pajak'),
+ 'npwp': vals.get('npwp'),
+ 'sppkp': vals.get('sppkp'),
+ 'alamat_lengkap_text': vals.get('alamat_lengkap_text'),
+ 'industry_id': vals.get('industry_id'),
+ 'company_type_id': vals.get('company_type_id')
+ }
+
+ # Lakukan update pada semua child secara rekursif
+ for child in children:
+ update_children_recursively(child, vals_for_child)
+
+ # Lakukan write untuk parent dengan vals asli
+ res = super(ResPartner, self).write(vals)
return res
+ # if self.company_type == 'person' and not partner.parent_id:
+ # if self.parent_id:
+ # parent = self.parent_id
+ # vals['industry_id'] = parent.industry_id.id
+ # vals['company_type_id'] = parent.company_type_id.id
+ #
+ # res = super(ResPartner, self).write(vals)
+ # return res
+
+ @api.depends('company_type', 'parent_id', 'npwp', 'sppkp', 'nama_wajib_pajak','alamat_lengkap_text', 'industry_id', 'company_type_id')
+ def _related_fields(self):
+ for partner in self:
+ if partner.company_type == 'person' and partner.parent_id:
+ partner.customer_type = partner.parent_id.customer_type
+ partner.npwp = partner.parent_id.npwp
+ partner.sppkp = partner.parent_id.sppkp
+ partner.nama_wajib_pajak = partner.parent_id.nama_wajib_pajak
+ partner.alamat_lengkap_text = partner.parent_id.alamat_lengkap_text
+ partner.industry_id = partner.parent_id.industry_id.id
+ partner.company_type_id = partner.parent_id.company_type_id.id
+
@api.constrains('property_payment_term_id')
def updated_by_payment_term(self):
for rec in self:
diff --git a/indoteknik_custom/models/res_users.py b/indoteknik_custom/models/res_users.py
index 33f64ce3..5e16aad1 100755
--- a/indoteknik_custom/models/res_users.py
+++ b/indoteknik_custom/models/res_users.py
@@ -11,6 +11,8 @@ 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')
+
def _generate_otp(self):
for user in self:
@@ -28,7 +30,22 @@ class ResUsers(models.Model):
user._generate_otp()
user._generate_activation_token()
template.send_mail(user.id, force_send=True)
-
+
+ def send_company_request_mail(self):
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_request')
+ for user in self:
+ template.send_mail(user.id, force_send=True)
+
+ def send_company_request_approve_mail(self):
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_request_approve')
+ for user in self:
+ template.send_mail(user.id, force_send=True)
+
+ def send_company_request_reject_mail(self):
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_request_reject')
+ for user in self:
+ template.send_mail(user.id, force_send=True)
+
def get_activation_token_url(self):
base_url = self.env['ir.config_parameter'].get_param('site.base.url')
return f'{base_url}/register?activation=token&amp;token={self.activation_token}'
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 710e99de..8f48e898 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -38,14 +38,14 @@ class SaleOrder(models.Model):
have_outstanding_po = fields.Boolean('Have Outstanding PO', compute='_have_outstanding_po')
purchase_ids = fields.Many2many('purchase.order', string='Purchases', compute='_get_purchases')
real_shipping_id = fields.Many2one(
- 'res.partner', string='Real Delivery Address', readonly=True, required=True,
+ 'res.partner', string='Real Delivery Address', readonly=True,
states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]},
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
- help="Dipakai untuk alamat tempel")
+ help="Dipakai untuk alamat tempel", tracking=True)
real_invoice_id = fields.Many2one(
'res.partner', string='Delivery Invoice Address', required=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
- help="Dipakai untuk alamat tempel")
+ help="Dipakai untuk alamat tempel", tracking=True)
fee_third_party = fields.Float('Fee Pihak Ketiga')
so_status = fields.Selection([
('terproses', 'Terproses'),
@@ -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,159 @@ 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)
+ unreserve_id = fields.Many2one('stock.picking', 'Unreserve Picking')
+ 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')
+ partner_invoice_id = fields.Many2one(
+ 'res.partner', string='Invoice Address',
+ readonly=True, required=True,
+ states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]},
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
+ tracking=True, # Menambahkan tracking=True
+ )
+ partner_shipping_id = fields.Many2one(
+ 'res.partner', string='Delivery Address', readonly=True, required=True,
+ states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]},
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
+
+ payment_term_id = fields.Many2one(
+ 'account.payment.term', string='Payment Terms', check_company=True, # Unrequired company
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
+
+ def action_estimate_shipping(self):
+ total_weight = 0
+ missing_weight_products = []
+
+ for line in self.order_line:
+ if line.weight:
+ total_weight += line.weight
+ else:
+ missing_weight_products.append(line.product_id.name)
+
+ if total_weight == 0:
+ raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
+
+ # Mendapatkan city_id berdasarkan nama kota
+ origin_city_name = self.warehouse_id.partner_id.kota_id.name
+ destination_city_name = self.real_shipping_id.kota_id.name
+
+ origin_city_id = self._get_city_id_by_name(origin_city_name)
+ destination_city_id = self._get_city_id_by_name(destination_city_name)
+
+ if not origin_city_id or not destination_city_id:
+ raise UserError("Gagal mendapatkan ID kota asal atau tujuan.")
+
+ result = self._call_rajaongkir_api(total_weight, origin_city_id, destination_city_id)
+ if result:
+ estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value']
+ self.delivery_amt = estimated_cost
+ self.message_post(body=f"Estimasi Ongkos Kirim: {estimated_cost}")
+ else:
+ raise UserError("Gagal mendapatkan estimasi ongkir.")
+
+ def _call_rajaongkir_api(self, total_weight, origin_city_id, destination_city_id):
+ url = 'https://pro.rajaongkir.com/api/cost'
+ headers = {
+ 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ }
+ courier = self.carrier_id.name.lower()
+
+ data = {
+ 'origin': int(origin_city_id),
+ 'originType': 'city',
+ 'destination': int(destination_city_id),
+ 'destinationType': 'city',
+ 'weight': int(total_weight * 1000),
+ 'courier': courier,
+ }
+
+ response = requests.post(url, headers=headers, data=data)
+ if response.status_code == 200:
+ return response.json()
+ return None
+
+ def _normalize_city_name(self, city_name):
+ # Ubah nama kota menjadi huruf kecil
+ city_name = city_name.lower()
+
+ # Hilangkan prefiks "kabupaten" atau "kota" jika ada
+ if city_name.startswith('kabupaten'):
+ city_name = city_name.replace('kabupaten', '').strip()
+ elif city_name.startswith('kota'):
+ city_name = city_name.replace('kota', '').strip()
+
+ # Hilangkan spasi yang berlebihan
+ city_name = " ".join(city_name.split())
+
+ return city_name
+
+ def _get_city_id_by_name(self, city_name):
+ url = 'https://pro.rajaongkir.com/api/city'
+ headers = {
+ 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ }
+
+ # Normalisasi nama kota sebelum melakukan pencarian
+ normalized_city_name = self._normalize_city_name(city_name)
+
+ response = requests.get(url, headers=headers)
+ if response.status_code == 200:
+ city_data = response.json()
+ for city in city_data['rajaongkir']['results']:
+ if city['city_name'].lower() == normalized_city_name:
+ return city['city_id']
+ return None
+
+ def _get_subdistrict_id_by_name(self, city_id, subdistrict_name):
+ url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}'
+ headers = {
+ 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ }
+
+ response = requests.get(url, headers=headers)
+ if response.status_code == 200:
+ subdistrict_data = response.json()
+ for subdistrict in subdistrict_data['rajaongkir']['results']:
+ if subdistrict['subdistrict_name'].lower() == subdistrict_name.lower():
+ return subdistrict['subdistrict_id']
+ return None
+
+
+ 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))
+
+ 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:
@@ -389,10 +544,22 @@ class SaleOrder(models.Model):
# return [('id', 'not in', order_ids)]
# return ['&', ('order_line.invoice_lines.move_id.move_type', 'in', ('out_invoice', 'out_refund')), ('order_line.invoice_lines.move_id', operator, value)]
- @api.onchange('partner_shipping_id')
- def onchange_partner_shipping(self):
- self.real_shipping_id = self.partner_shipping_id
- self.real_invoice_id = self.partner_invoice_id
+
+ def check_data_real_delivery_address(self):
+ real_delivery_address = self.real_shipping_id
+
+ if not real_delivery_address.state_id:
+ raise UserError('State Real Delivery Address harus diisi')
+ if not real_delivery_address.zip:
+ raise UserError('Zip code Real Delivery Address harus diisi')
+ if not real_delivery_address.mobile:
+ raise UserError('Mobile Real Delivery Address harus diisi')
+ if not real_delivery_address.phone:
+ raise UserError('Phone Real Delivery Address harus diisi')
+ if not real_delivery_address.kecamatan_id:
+ raise UserError('Kecamatan Real Delivery Address harus diisi')
+ if not real_delivery_address.kelurahan_id:
+ raise UserError('Kelurahan Real Delivery Address harus diisi')
@api.onchange('partner_id')
def onchange_partner_contact(self):
@@ -630,6 +797,12 @@ 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 not order.real_shipping_id:
+ UserError('Real Delivery Address harus di isi')
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 +916,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 +953,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 +1016,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 +1047,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)
@@ -942,3 +1165,38 @@ class SaleOrder(models.Model):
order_line.tax_id = tax_id
order_line.discount = discount
order_line.order_id.use_button = True
+
+ @api.model
+ def create(self, vals):
+ # Ensure partner details are updated when a sale order is created
+ order = super(SaleOrder, self).create(vals)
+ order._update_partner_details()
+ return order
+
+ def write(self, vals):
+ # Call the super method to handle the write operation
+ res = super(SaleOrder, self).write(vals)
+
+ # Check if the update is coming from a save operation
+ if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']):
+ self._update_partner_details()
+
+ return res
+
+ def _update_partner_details(self):
+ for order in self:
+ partner = order.partner_id.parent_id or order.partner_id
+ if partner:
+ # Update partner details
+ partner.sppkp = order.sppkp
+ partner.npwp = order.npwp
+ partner.email = order.email
+ partner.customer_type = order.customer_type
+
+ # Save changes to the partner record
+ partner.write({
+ 'sppkp': partner.sppkp,
+ 'npwp': partner.npwp,
+ 'email': partner.email,
+ 'customer_type': partner.customer_type,
+ }) \ No newline at end of file
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index a64a744c..d1dcd0af 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -31,6 +31,7 @@ class SaleOrderLine(models.Model):
qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved')
reserved_from = fields.Char(string='Reserved From', copy=False)
item_percent_margin_without_deduction = fields.Float('%Margin', compute='_compute_item_margin_without_deduction')
+ weight = fields.Float(string='Weight')
@api.constrains('note_procurement')
def note_procurement_to_apo(self):
@@ -257,6 +258,7 @@ class SaleOrderLine(models.Model):
('(' + attribute_values_str + ')' if attribute_values_str else '') + ' ' + \
(line.product_id.short_spesification if line.product_id.short_spesification else '')
line.name = line_name
+ line.weight = line.product_id.weight
def compute_delivery_amt_line(self):
for line in self:
@@ -348,7 +350,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 d8dec47c..1eb6f31b 100644
--- a/indoteknik_custom/models/solr/product_template.py
+++ b/indoteknik_custom/models/solr/product_template.py
@@ -112,8 +112,8 @@ class ProductTemplate(models.Model):
"description_clean_t": cleaned_desc or '',
'has_product_info_b': True,
'publish_b': not template.unpublished,
- 'sni_b': template.unpublished,
- 'tkdn_b': template.unpublished,
+ 'sni_b': template.sni,
+ 'tkdn_b': template.tkdn,
"qty_sold_f": template.qty_sold,
"is_in_bu_b": is_in_bu,
"voucher_min_purchase_f" : voucher.min_purchase_amount or 0,
diff --git a/indoteknik_custom/models/solr/promotion_program_line.py b/indoteknik_custom/models/solr/promotion_program_line.py
index 3e3a2a28..64ad4209 100644
--- a/indoteknik_custom/models/solr/promotion_program_line.py
+++ b/indoteknik_custom/models/solr/promotion_program_line.py
@@ -53,6 +53,11 @@ class PromotionProgramLine(models.Model):
'package_limit_user_i': rec.package_limit_user,
'package_limit_trx_i': rec.package_limit_trx,
'price_f': rec.price,
+ 'price_tier_1_f': rec.price_tier_1,
+ 'price_tier_2_f': rec.price_tier_2,
+ 'price_tier_3_f': rec.price_tier_3,
+ 'price_tier_4_f': rec.price_tier_4,
+ 'price_tier_5_f': rec.price_tier_5,
'sequence_i': sequence_value,
'product_ids': [x.product_id.id for x in rec.product_ids],
'products_s': json.dumps(products),
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_move.py b/indoteknik_custom/models/stock_move.py
index fe46bf65..ac2e3cc0 100644
--- a/indoteknik_custom/models/stock_move.py
+++ b/indoteknik_custom/models/stock_move.py
@@ -1,5 +1,6 @@
from odoo import fields, models, api
-
+from odoo.tools.misc import format_date, OrderedSet
+from odoo.exceptions import UserError
class StockMove(models.Model):
_inherit = 'stock.move'
@@ -7,6 +8,47 @@ class StockMove(models.Model):
line_no = fields.Integer('No', default=0)
sale_id = fields.Many2one('sale.order', string='SO')
+ def _do_unreserve(self, product=None, quantity=False):
+ moves_to_unreserve = OrderedSet()
+ for move in self:
+ if move.state == 'cancel' or (move.state == 'done' and move.scrapped):
+ continue
+ elif move.state == 'done':
+ raise UserError("You cannot unreserve a stock move that has been set to 'Done'.")
+
+ if product and move.product_id != product:
+ continue # Skip moves that don't match the specified product
+ moves_to_unreserve.add(move.id)
+
+ moves_to_unreserve = self.env['stock.move'].browse(moves_to_unreserve)
+
+ ml_to_update, ml_to_unlink = OrderedSet(), OrderedSet()
+ moves_not_to_recompute = OrderedSet()
+
+ for ml in moves_to_unreserve.move_line_ids:
+ if product and ml.product_id != product:
+ continue # Only affect the specified product
+
+ if quantity and quantity > 0:
+ # Only reduce by the specified quantity if it is greater than zero
+ ml_to_update.add(ml.id)
+ remaining_qty = ml.product_uom_qty - quantity
+ ml.write({'product_uom_qty': remaining_qty if remaining_qty > 0 else 0})
+ quantity = 0 # Set to zero to prevent further unreserving in the same loop
+ elif ml.qty_done:
+ ml_to_update.add(ml.id)
+ else:
+ ml_to_unlink.add(ml.id)
+ moves_not_to_recompute.add(ml.move_id.id)
+
+ ml_to_update, ml_to_unlink = self.env['stock.move.line'].browse(ml_to_update), self.env['stock.move.line'].browse(ml_to_unlink)
+ moves_not_to_recompute = self.env['stock.move'].browse(moves_not_to_recompute)
+
+ ml_to_unlink.unlink()
+ (moves_to_unreserve - moves_not_to_recompute)._recompute_state()
+ return True
+
+
def _prepare_account_move_line_from_mr(self, po_line, qty, move=False):
po_line.ensure_one()
aml_currency = move and move.currency_id or po_line.currency_id
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index d16d508e..66a326ff 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -125,12 +125,27 @@ class StockPicking(models.Model):
raise UserError('Hanya Logistic yang bisa mengubah shipping method')
def do_unreserve(self):
+ if not self._context.get('darimana') == 'sale.order':
+ self.sale_id.unreserve_id = self.id
+ return self._create_approval_notification('Logistic')
+
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
+
+ def _create_approval_notification(self, approval_role):
+ title = 'Warning'
+ message = f'Butuh approval sales untuk unreserved'
+ return self._create_notification_action(title, message)
+
+ def _create_notification_action(self, title, message):
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': { 'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'} },
+ }
def _compute_shipping_status(self):
for rec in self:
@@ -373,6 +388,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/user_company_request.py b/indoteknik_custom/models/user_company_request.py
index 2467261a..d540b0f6 100644
--- a/indoteknik_custom/models/user_company_request.py
+++ b/indoteknik_custom/models/user_company_request.py
@@ -1,6 +1,6 @@
from odoo import models, fields
from odoo.exceptions import UserError
-
+from odoo.http import request
class UserCompanyRequest(models.Model):
_name = 'user.company.request'
@@ -15,6 +15,8 @@ class UserCompanyRequest(models.Model):
], string='Approval')
def write(self, vals):
+ user = self.get_user_by_email(self.user_id.email)
+ user.parent_name = self.user_input
is_approve = vals.get('is_approve')
if self.is_approve and is_approve:
raise UserError('Tidak dapat mengubah approval yang sudah diisi')
@@ -22,10 +24,28 @@ class UserCompanyRequest(models.Model):
if not self.is_approve and is_approve:
if is_approve == 'approved':
self.user_id.parent_id = self.user_company_id.id
+ self.user_id.customer_type = self.user_company_id.customer_type
+ self.user_id.npwp = self.user_company_id.npwp
+ self.user_id.sppkp = self.user_company_id.sppkp
+ self.user_id.nama_wajib_pajak = self.user_company_id.nama_wajib_pajak
+ self.user_id.alamat_lengkap_text = self.user_company_id.alamat_lengkap_text
+ self.user_id.industry_id = self.user_company_id.industry_id.id
+ self.user_id.company_type_id = self.user_company_id.company_type_id.id
+ self.user_id.user_id = self.user_company_id.user_id
+ self.user_id.property_account_receivable_id = self.user_company_id.property_account_receivable_id
+ self.user_id.property_account_payable_id = self.user_company_id.property_account_payable_id
+ self.user_company_id.active = True
+ user.send_company_request_approve_mail()
else:
new_company = self.env['res.partner'].create({
'name': self.user_input
})
- self.user_id.parent_id = new_company.id
+ # self.user_id.parent_id = new_company.id
+ user.send_company_request_reject_mail()
return super(UserCompanyRequest, self).write(vals)
- \ No newline at end of file
+
+ def get_user_by_email(self, email):
+ return request.env['res.users'].search([
+ ('login', '=', email),
+ ('active', 'in', [True, False])
+ ]) \ No newline at end of file
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 0af22d47..169f4a6b 100644
--- a/indoteknik_custom/models/website_user_cart.py
+++ b/indoteknik_custom/models/website_user_cart.py
@@ -1,4 +1,4 @@
-from odoo import fields, models
+from odoo import fields, models, api
from datetime import datetime, timedelta
class WebsiteUserCart(models.Model):
@@ -16,6 +16,26 @@ class WebsiteUserCart(models.Model):
], 'Source', default='add_to_cart')
user_other_carts = fields.One2many('website.user.cart', 'id', 'Other Products', compute='_compute_user_other_carts')
is_reminder = fields.Boolean(string='Reminder?')
+ phone_user = fields.Char(string='Phone', related='user_id.mobile')
+ price = fields.Float(string='Price', compute='_compute_price')
+ program_product_id = fields.Many2one('product.product', string='Program Products', compute='_compute_program_product_ids')
+
+ @api.depends('program_line_id')
+ def _compute_program_product_ids(self):
+ for record in self:
+ if record.program_line_id and record.program_line_id.product_ids:
+ product = record.program_line_id.product_ids[0]
+ record.program_product_id = product.product_id
+ record.product_id = product.product_id
+ else:
+ # Handle case where there are no product_ids
+ record.program_product_id = False
+
+ def _compute_price(self):
+ for record in self:
+ record.price = record.get_price_website(record.product_id.id)['price']
+ if record.program_line_id:
+ record.price = record.get_price_coret(record.program_line_id.id)
def _compute_user_other_carts(self):
for record in self:
@@ -130,8 +150,7 @@ class WebsiteUserCart(models.Model):
if voucher_shipping:
voucher_shipping_info = voucher_shipping.apply(order_line)
- discount_voucher_shipping = voucher_shipping_info['discount']['all']
- subtotal -= discount_voucher_shipping
+ discount_voucher_shipping = voucher_shipping_info['discount']['all']
tax = round(subtotal * 0.11)
grand_total = subtotal + tax
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 5e7554a5..19e3bdca 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
@@ -134,3 +135,6 @@ access_shipment_group,access.shipment.group,model_shipment_group,,1,1,1,1
access_shipment_group_line,access.shipment.group.line,model_shipment_group_line,,1,1,1,1
access_sales_order_reject,access.sales.order.reject,model_sales_order_reject,,1,1,1,1
access_approval_date_doc,access.approval.date.doc,model_approval_date_doc,,1,1,1,1
+access_account_tax,access.account.tax,model_account_tax,,1,1,1,1
+access_approval_unreserve,access.approval.unreserve,model_approval_unreserve,,1,1,1,1
+access_approval_unreserve_line,access.approval.unreserve.line,model_approval_unreserve_line,,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/account_move_views.xml b/indoteknik_custom/views/account_move_views.xml
index 1c70cc7b..4acafb14 100644
--- a/indoteknik_custom/views/account_move_views.xml
+++ b/indoteknik_custom/views/account_move_views.xml
@@ -1,117 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
- <record id="due_extension_tree" model="ir.ui.view">
- <field name="name">due.extension.tree</field>
- <field name="model">due.extension</field>
- <field name="arch" type="xml">
- <tree default_order="create_date desc" create="0">
- <field name="number"/>
- <field name="partner_id"/>
- <field name="day_extension"/>
- <field name="description"/>
- <field name="approval_status"/>
- <field name="is_approve"/>
- </tree>
- </field>
- </record>
+ <data>
+ <record id="due_extension_tree" model="ir.ui.view">
+ <field name="name">due.extension.tree</field>
+ <field name="model">due.extension</field>
+ <field name="arch" type="xml">
+ <tree default_order="create_date desc" create="0">
+ <field name="number"/>
+ <field name="partner_id"/>
+ <field name="day_extension"/>
+ <field name="description"/>
+ <field name="approval_status"/>
+ <field name="is_approve"/>
+ </tree>
+ </field>
+ </record>
- <record id="due_extension_line_tree" model="ir.ui.view">
- <field name="name">due.extension.line.tree</field>
- <field name="model">due.extension.line</field>
- <field name="arch" type="xml">
- <tree>
- <field name="partner_id"/>
- <field name="invoice_id"/>
- <field name="date_invoice"/>
- <field name="due_date"/>
- <field name="day_to_due"/>
- <field name="efaktur_id"/>
- <field name="reference"/>
- <field name="total_amt"/>
- <field name="open_amt"/>
- </tree>
- </field>
- </record>
+ <record id="due_extension_line_tree" model="ir.ui.view">
+ <field name="name">due.extension.line.tree</field>
+ <field name="model">due.extension.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="partner_id"/>
+ <field name="invoice_id"/>
+ <field name="date_invoice"/>
+ <field name="due_date"/>
+ <field name="day_to_due"/>
+ <field name="efaktur_id"/>
+ <field name="reference"/>
+ <field name="total_amt"/>
+ <field name="open_amt"/>
+ </tree>
+ </field>
+ </record>
- <record id="due_extension_form" model="ir.ui.view">
- <field name="name">due.extension.form</field>
- <field name="model">due.extension</field>
- <field name="arch" type="xml">
- <form create="false">
- <header>
- <button name="approve_new_due"
- string="Approve"
- type="object"
- />
- <button name="due_extension_approval"
- string="Ask Approval"
+ <record id="due_extension_form" model="ir.ui.view">
+ <field name="name">due.extension.form</field>
+ <field name="model">due.extension</field>
+ <field name="arch" type="xml">
+ <form create="false">
+ <header>
+ <button name="approve_new_due"
+ string="Approve"
type="object"
- />
- <button name="due_extension_cancel"
- string="Cancel"
- type="object"
- />
- </header>
- <sheet>
- <group>
+ />
+ <button name="due_extension_approval"
+ string="Ask Approval"
+ type="object"
+ />
+ <button name="due_extension_cancel"
+ string="Cancel"
+ type="object"
+ />
+ </header>
+ <sheet>
<group>
- <field name="partner_id" readonly="1"/>
- <field name="day_extension" attrs="{'readonly': [('is_approve', '=', True)]}"/>
+ <group>
+ <field name="partner_id" readonly="1"/>
+ <field name="day_extension" attrs="{'readonly': [('is_approve', '=', True)]}"/>
+ </group>
+ <group>
+ <field name="is_approve" readonly="1"/>
+ <field name="order_id" readonly="1"/>
+ <field name="counter" readonly="1"/>
+ <field name="approval_status" readonly="1"/>
+ </group>
</group>
<group>
- <field name="is_approve" readonly="1"/>
- <field name="order_id" readonly="1"/>
- <field name="counter" readonly="1"/>
- <field name="approval_status" readonly="1"/>
+ <field name="description" attrs="{'readonly': [('approval_status', '=', 'approved')]}"/>
</group>
- </group>
- <group>
- <field name="description" attrs="{'readonly': [('approval_status', '=', 'approved')]}"/>
- </group>
- <notebook>
- <page string="Invoices">
- <field name="due_line" attrs="{'readonly': [('is_approve', '=', True)]}"/>
- </page>
- </notebook>
- </sheet>
- <div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
- <field name="message_ids" widget="mail_thread"/>
- </div>
- </form>
- </field>
- </record>
+ <notebook>
+ <page string="Invoices">
+ <field name="due_line" attrs="{'readonly': [('is_approve', '=', True)]}"/>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
- <record id="due_extension_view_search" model="ir.ui.view">
- <field name="name">due.extension.search.view</field> <!-- Made the name more descriptive -->
- <field name="model">due.extension</field>
- <field name="arch" type="xml">
- <search string="Search Due Extension">
- <field name="number"/>
- <field name="partner_id"/>
- <field name="invoice_id"/>
- <field name="order_id"/>
- </search>
- </field>
- </record>
+ <record id="due_extension_view_search" model="ir.ui.view">
+ <field name="name">due.extension.search.view</field> <!-- Made the name more descriptive -->
+ <field name="model">due.extension</field>
+ <field name="arch" type="xml">
+ <search string="Search Due Extension">
+ <field name="number"/>
+ <field name="partner_id"/>
+ <field name="invoice_id"/>
+ <field name="order_id"/>
+ </search>
+ </field>
+ </record>
- <record id="due_extension_action" model="ir.actions.act_window">
- <field name="name">Due Extension</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">due.extension</field>
- <field name="view_mode">tree,form</field>
- </record>
+ <record id="due_extension_action" model="ir.actions.act_window">
+ <field name="name">Due Extension</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">due.extension</field>
+ <field name="view_mode">tree,form</field>
+ </record>
- <menuitem
- id="menu_due_extension"
- name="Due Extension"
- parent="sale.product_menu_catalog"
- sequence="4"
- action="due_extension_action"
- />
- <menuitem id="menu_due_extension" name="Due Extension"
- parent="account.menu_finance_receivables"
- action="due_extension_action"
- sequence="100"
- />
+ <menuitem
+ id="menu_due_extension"
+ name="Due Extension"
+ parent="sale.product_menu_catalog"
+ sequence="4"
+ action="due_extension_action"
+ />
+ <menuitem id="menu_due_extension" name="Due Extension"
+ parent="account.menu_finance_receivables"
+ action="due_extension_action"
+ sequence="100"
+ />
+
+ <record id="mail_template_due_extension_approve" model="mail.template">
+ <field name="name">Due Extension: Approve</field>
+ <field name="model_id" ref="indoteknik_custom.model_due_extension"/>
+ <field name="subject">Approval for Due Extension: DE ${object.name}</field>
+ <field name="email_from">"PT. Indoteknik Dotcom Gemilang" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">anto@indoteknik.co.id</field>
+ <field name="email_to"> ${object.order_id.user_id.login | safe}</field>
+ <field name="body_html" type="html">
+ <p>Dear Anto,</p>
+
+ <p>We would like to inform you that the due extension for the following document has been approved:</p>
+
+ <table border="0" cellpadding="5" cellspacing="0" width="15%">
+ <tr>
+ <td><strong>DE Number:</strong></td>
+ <td>${object.number}</td>
+ </tr>
+ <tr>
+ <td><strong>SO Number:</strong></td>
+ <td>${object.order_id.name}</td>
+ </tr>
+ <tr>
+ <td><strong>New Due Date:</strong></td>
+ <td>${object.due_line[0].invoice_id.new_due_date}</td>
+ </tr>
+ <tr>
+ <td><strong>Extension (days):</strong></td>
+ <td>${object.day_extension}</td>
+ </tr>
+ </table>
+
+ <p>If you have any further questions or need additional information, please feel free to contact us.</p>
+
+ <p>Best regards,</p>
+ <p><strong>PT. Indoteknik Dotcom Gemilang</strong></p>
+ </field>
+ </record>
+ </data>
</odoo>
diff --git a/indoteknik_custom/views/approval_unreserve.xml b/indoteknik_custom/views/approval_unreserve.xml
new file mode 100644
index 00000000..317b5276
--- /dev/null
+++ b/indoteknik_custom/views/approval_unreserve.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+ <record id="view_approval_unreserve_tree" model="ir.ui.view">
+ <field name="name">approval.unreserve.tree</field>
+ <field name="model">approval.unreserve</field>
+ <field name="arch" type="xml">
+ <tree string="Approval Unreserve">
+ <field name="number"/>
+ <field name="request_date"/>
+ <field name="picking_id"/>
+ <field name="state"/>
+ <field name="user_id"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_line_tree" model="ir.ui.view">
+ <field name="name">approval.unreserve.line.tree</field>
+ <field name="model">approval.unreserve.line</field>
+ <field name="arch" type="xml">
+ <tree editable="bottom">
+ <field name="move_id"/>
+ <field name="product_id"/>
+ <field name="dest_picking_id"/>
+ <field name="sales_id"/>
+ <field name="unreserve_qty"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_form" model="ir.ui.view">
+ <field name="name">approval.unreserve.form</field>
+ <field name="model">approval.unreserve</field>
+ <field name="arch" type="xml">
+ <form create="false">
+ <header>
+ <button name="action_submit_for_approval" type="object" string="Submit for Approval" attrs="{'invisible': [('state', '!=', 'draft')]}"/>
+ <button name="action_approve" type="object" string="Approve" attrs="{'invisible': [('state', '!=', 'waiting_approval')]}"/>
+ <button name="action_reject" type="object" string="Reject" attrs="{'invisible': [('state', '!=', 'waiting_approval')]}"/>
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="number" readonly="1"/>
+ <field name="request_date" readonly="1"/>
+ <field name="picking_id"/>
+ <field name="user_id" readonly="1"/>
+ <field name="state" readonly="1"/>
+ <field name="approved_by" readonly="1"/>
+ <field name="reason"/>
+ </group>
+ </group>
+ <notebook>
+ <page string="Lines">
+ <field name="approval_line"/>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_action" model="ir.actions.act_window">
+ <field name="name">Approval Unreserve</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">approval.unreserve</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_approval_unreserve"
+ name="Approval Unreserve"
+ parent="sale.product_menu_catalog"
+ sequence="4"
+ action="approval_unreserve_action"
+ />
+</odoo>
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 dd5bb120..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,6 +72,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/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index b2768c71..dd501d8c 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -20,6 +20,16 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+
+ <record id="sequence_approval_unreserve" model="ir.sequence">
+ <field name="name">Approval Unreserve</field>
+ <field name="code">approval.unreserve</field>
+ <field name="active">TRUE</field>
+ <field name="prefix">AU/%(year)s/</field>
+ <field name="padding">5</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
<record id="sequence_logbook_sj" model="ir.sequence">
<field name="name">Logbook SJ</field>
diff --git a/indoteknik_custom/views/product_template.xml b/indoteknik_custom/views/product_template.xml
index b6155eea..a77b99de 100755
--- a/indoteknik_custom/views/product_template.xml
+++ b/indoteknik_custom/views/product_template.xml
@@ -22,7 +22,7 @@
<field name="last_update_solr" readonly="1" />
</field>
<field name="public_categ_ids" position="attributes">
- <attribute name="required">1</attribute>
+ <attribute name="required">0</attribute>
</field>
<field name="public_categ_ids" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
diff --git a/indoteknik_custom/views/promotion/promotion_program_line.xml b/indoteknik_custom/views/promotion/promotion_program_line.xml
index f3c2eea1..9cda67a8 100644
--- a/indoteknik_custom/views/promotion/promotion_program_line.xml
+++ b/indoteknik_custom/views/promotion/promotion_program_line.xml
@@ -32,6 +32,11 @@
<field name="package_limit_user" />
<field name="package_limit_trx" />
<field name="price" attrs="{'invisible': [('promotion_type', '=', 'special_price')]}" />
+ <field name="price_tier_1"/>
+ <field name="price_tier_2" invisible="1"/>
+ <field name="price_tier_3" invisible="1"/>
+ <field name="price_tier_4" invisible="1"/>
+ <field name="price_tier_5" invisible="1"/>
<field name="sequence"/>
<field name="discount_type" attrs="{'invisible': [('promotion_type', '!=', 'special_price')]}" />
<field name="discount_amount" attrs="{'invisible': [('promotion_type', '!=', 'special_price')]}" />
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index c301f3d3..f6e5a1a4 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -26,15 +26,21 @@
<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" attrs="{'invisible': [('state', 'not in', ('purchase', 'done'))]}"/>
+ </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"/>
+ </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"/>
@@ -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/res_users.xml b/indoteknik_custom/views/res_users.xml
index 976f46c9..39b9d43e 100644
--- a/indoteknik_custom/views/res_users.xml
+++ b/indoteknik_custom/views/res_users.xml
@@ -4,7 +4,8 @@
<field name="name">Users: Activation Request</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="subject">Aktivasi Akun - Indoteknik.com</field>
- <field name="email_from">sales@indoteknik.com</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">noreply@indoteknik.com</field>
<field name="email_to">${object.login | safe}</field>
<field name="body_html" type="html">
<table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;">
@@ -59,5 +60,199 @@
</table>
</field>
</record>
+ <record id="mail_template_res_user_company_request" model="mail.template">
+ <field name="name">Users: Company Request</field>
+ <field name="model_id" ref="base.model_res_users"/>
+ <field name="subject">Email Pendaftaran Bisnis dalam Proses Review</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">noreply@indoteknik.com</field>
+ <field name="email_to">${object.login | safe}</field>
+ <field name="body_html" type="html">
+ <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;">
+ <tr><td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
+ <!-- HEADER -->
+ <tbody>
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="middle">
+ <span></span>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2" style="text-align:center;">
+ <hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr><td style="padding-bottom: 24px;"><b>Halo ${object.parent_name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas kepercayaan Anda dengan mendaftarkan bisnis Anda di Indoteknik.com. Permohonan Anda saat ini sedang dalam proses review oleh tim kami.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Saat ini, kami sedang melakukan pengecekan akhir pada data yang Anda berikan. Proses ini biasanya memakan waktu sekitar 2 x 24 jam.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Setelah proses review selesai, kami akan segera menginformasikan status akun bisnis Anda melalui email.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Jika ada pertanyaan lebih lanjut, jangan ragu untuk menghubungi kami di <a href="mailto:sales@indoteknik.com">sales@indoteknik.com</a> atau hubungi whatsapp kami di <a href="https://wa.me/6281717181922">0817-1718-1922</a></td></tr>
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatiannya.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><strong>Hormat kami,</strong></td></tr>
+ <tr><td style="padding-bottom: 2px;">Indoteknik.com</td></tr>
+ <tr>
+ <td style="text-align:center;">
+ <hr width="100%"
+ style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ </tbody>
+ </table>
+ </td></tr>
+ </table>
+ </field>
+ </record>
+ <record id="mail_template_res_user_company_request_approve" model="mail.template">
+ <field name="name">Users: Company Request Approve</field>
+ <field name="model_id" ref="base.model_res_users"/>
+ <field name="subject">Email Pendaftaran Bisnis Berhasil</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">noreply@indoteknik.com</field>
+ <field name="email_to">${object.login | safe}</field>
+ <field name="body_html" type="html">
+ <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;">
+ <tr><td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
+ <!-- HEADER -->
+ <tbody>
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="middle">
+ <span></span>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2" style="text-align:center;">
+ <hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr><td style="padding-bottom: 24px;"><b>Yth. ${object.parent_name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Selamat! Pendaftaran akun bisnis Anda di indoteknik.com telah <b>berhasil diverifikasi &amp; sudah aktif.</b> kini anda dapat menikmati berbagai kemudahan dalam berbelanja, antara lain:</td></tr>
+ <tr><td style="padding-bottom: 16px;"><b>Fitur Faktur Pajak &amp; Invoice:</b> Dengan mudah download faktur pajak &amp; Invoice Anda secara digital.</td></tr>
+ <tr><td style="padding-bottom: 16px;"><b>Pembayaran Lengkap:</b> Pilih metode pembayaran yang sesuai dengan kebutuhan Anda, baik transfer bank, VA, kartu kredit, atau pembayaran tempo.</td></tr>
+ <tr><td style="padding-bottom: 16px;"><b>Pelacakan Pengiriman:</b> Lacak status pengiriman pesanan Anda secara real-time.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Untuk memulai transaksi, silakan login Kembali menggunakan akun Anda di <a href="https://indoteknik.com/login">Indoteknik.com</a></td></tr>
+ <tr><td style="padding-bottom: 16px;">Kami sangat senang dapat melayani Anda. Jika ada pertanyaan atau membutuhkan bantuan, jangan ragu untuk menghubungi tim layanan Customer kami di Whatsapp <a href="https://wa.me/6281717181922">0817-1718-1922</a> atau <a href="mailto:sales@indoteknik.com">sales@indoteknik.com</a></td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">Indoteknik.com</td></tr>
+ <tr>
+ <td style="text-align:center;">
+ <hr width="100%"
+ style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ </tbody>
+ </table>
+ </td></tr>
+ </table>
+ </field>
+ </record>
+ <record id="mail_template_res_user_company_request_reject" model="mail.template">
+ <field name="name">Users: Company Request Reject</field>
+ <field name="model_id" ref="base.model_res_users"/>
+ <field name="subject">Email Pendaftaran Bisnis Tidak Berhasil</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">noreply@indoteknik.com</field>
+ <field name="email_to">${object.login | safe}</field>
+ <field name="body_html" type="html">
+ <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;">
+ <tr><td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
+ <!-- HEADER -->
+ <tbody>
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="middle">
+ <span></span>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2" style="text-align:center;">
+ <hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr><td style="padding-bottom: 24px;"><b>Yth. ${object.parent_name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas minat Anda untuk mendaftar akun bisnis di Indoteknik.com. Kami telah menerima permohonan pendaftaran Anda dan saat ini sedang dalam proses review.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Namun, setelah kami lakukan pengecekan, kami menemukan bahwa beberapa informasi yang Anda berikan masih belum lengkap. Untuk dapat melanjutkan proses pendaftaran, mohon kiranya Anda dapat melengkapi data-data berikut:</td></tr>
+ <tr>
+ <td style="padding-bottom: 8px; ">Informasi yang kami butuhkan saat ini adalah:</td>
+ </tr>
+ <tr>
+ <td style="padding: 4px 0; text-align: left;">
+ <ol style="padding-left: 20px; margin: 0;">
+ <li style="padding-bottom: 4px;">Detail Nama Bisnis</li>
+ <li style="padding-bottom: 4px;">Dokumen NPWP</li>
+ <li style="padding-bottom: 4px;">Dokumen SPPKP/Surat Pengukuhan Kena Pajak</li>
+ </ol>
+ </td>
+ </tr>
+
+ <tr><td style="padding-bottom: 16px;">Anda dapat mengirimkan informasi yang kurang tersebut melalui email ini atau dengan menghubungi tim layanan pelanggan kami di <a href="https://wa.me/6281717181922">0817-1718-1922</a> atau <a href="mailto:sales@indoteknik.com">sales@indoteknik.com</a>.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Kami mohon maaf atas ketidaknyamanan ini dan berharap dapat segera menyelesaikan proses pendaftaran akun bisnis Anda.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatiannya.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">Indoteknik.com</td></tr>
+ <tr>
+ <td style="text-align:center;">
+ <hr width="100%"
+ style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ </tbody>
+ </table>
+ </td></tr>
+ </table>
+ </field>
+ </record>
</data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 1257ff85..17faaa95 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,18 +70,22 @@
<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"/>
<field name="npwp" placeholder='99.999.999.9-999.999' required="1"/>
<field name="sppkp" attrs="{'required': [('customer_type', '=', 'pkp')]}"/>
<field name="email" required="1"/>
+ <field name="unreserve_id"/>
<field name="due_id" readonly="1"/>
<field name="source_id" domain="[('id', 'in', [32, 59, 60, 61])]" required="1"/>
<button name="override_allow_create_invoice"
string="Override Create Invoice"
type="object"
/>
+ <button string="Estimate Shipping" type="object" name="action_estimate_shipping"/>
</field>
<field name="partner_shipping_id" position="after">
<field name="real_shipping_id"/>
@@ -112,9 +128,11 @@
"/>
<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"/>
+ <field name="weight" optional="hide"/>
<field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide"/>
<field name="order_promotion_id" string="Promotion" readonly="1" optional="hide"/>
</xpath>
@@ -138,6 +156,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/user_company_request.xml b/indoteknik_custom/views/user_company_request.xml
index 2efc1e9b..c9edd3f8 100644
--- a/indoteknik_custom/views/user_company_request.xml
+++ b/indoteknik_custom/views/user_company_request.xml
@@ -4,7 +4,7 @@
<field name="name">user.company.request.tree</field>
<field name="model">user.company.request</field>
<field name="arch" type="xml">
- <tree create="0" default_order="is_approve desc">
+ <tree create="0" default_order="create_date desc">
<field name="user_id"/>
<field name="user_company_id"/>
<field name="user_input"/>
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"
diff --git a/indoteknik_custom/views/website_user_cart.xml b/indoteknik_custom/views/website_user_cart.xml
index 11573121..b4fb02be 100755
--- a/indoteknik_custom/views/website_user_cart.xml
+++ b/indoteknik_custom/views/website_user_cart.xml
@@ -11,13 +11,17 @@
<field name="model">website.user.cart</field>
<field name="arch" type="xml">
<tree>
+ <field name="create_date"/>
<field name="user_id"/>
<field name="product_id"/>
+ <field name="phone_user"/>
+ <field name="price"/>
<field name="program_line_id"/>
<field name="qty"/>
<field name="is_selected"/>
<field name="is_reminder"/>
<field name="source"/>
+ <field name="program_product_id" optional="hide"/>
</tree>
</field>
</record>
@@ -37,6 +41,9 @@
<field name="is_selected" />
<field name="is_reminder" />
<field name="source" />
+ <field name="phone_user"/>
+ <field name="program_product_id"/>
+ <field name="price"/>
</group>
<group></group>
</group>