summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2025-01-15 15:43:42 +0700
committerit-fixcomart <it@fixcomart.co.id>2025-01-15 15:43:42 +0700
commitbe1ee2092fef86e79932206cc48d5a146107ac32 (patch)
treee54a8c6ffda996f212ab1c2d03b27a32bab8ba89
parent8b2897d9c72eb67382221d320d488543aea08323 (diff)
parente4ec9406cd0903db59cfed34781da55a2dba4ca3 (diff)
Merge branch 'odoo-production' into iman/switch-account
-rw-r--r--indoteknik_api/controllers/api_v1/flash_sale.py2
-rw-r--r--indoteknik_api/controllers/api_v1/invoice.py25
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py341
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py6
-rw-r--r--indoteknik_api/controllers/api_v1/state.py6
-rw-r--r--indoteknik_api/controllers/api_v1/user.py14
-rw-r--r--indoteknik_api/controllers/controller.py30
-rw-r--r--indoteknik_api/models/__init__.py1
-rw-r--r--indoteknik_api/models/account_move.py5
-rw-r--r--indoteknik_api/models/product_product.py12
-rw-r--r--indoteknik_api/models/product_template.py6
-rw-r--r--indoteknik_api/models/res_partner.py170
-rw-r--r--indoteknik_api/models/res_users.py17
-rw-r--r--indoteknik_api/models/sale_order.py7
-rwxr-xr-xindoteknik_custom/__manifest__.py5
-rwxr-xr-xindoteknik_custom/models/__init__.py5
-rw-r--r--indoteknik_custom/models/account_move.py37
-rw-r--r--indoteknik_custom/models/coretax_fatur.py121
-rwxr-xr-xindoteknik_custom/models/product_template.py130
-rw-r--r--indoteknik_custom/models/promotion/sale_order.py11
-rwxr-xr-xindoteknik_custom/models/purchase_order.py148
-rwxr-xr-xindoteknik_custom/models/purchase_order_line.py74
-rw-r--r--indoteknik_custom/models/requisition.py6
-rw-r--r--indoteknik_custom/models/res_partner.py246
-rwxr-xr-xindoteknik_custom/models/res_users.py5
-rwxr-xr-xindoteknik_custom/models/sale_order.py60
-rw-r--r--indoteknik_custom/models/sale_order_line.py26
-rw-r--r--indoteknik_custom/models/solr/apache_solr.py2
-rw-r--r--indoteknik_custom/models/stock_immediate_transfer.py36
-rw-r--r--indoteknik_custom/models/stock_move.py21
-rw-r--r--indoteknik_custom/models/stock_picking.py338
-rw-r--r--indoteknik_custom/models/stock_picking_return.py14
-rw-r--r--indoteknik_custom/models/user_company_request.py38
-rw-r--r--indoteknik_custom/models/user_pengajuan_tempo.py134
-rw-r--r--indoteknik_custom/models/user_pengajuan_tempo_line.py12
-rw-r--r--indoteknik_custom/models/user_pengajuan_tempo_request.py630
-rw-r--r--indoteknik_custom/models/wati.py63
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv10
-rw-r--r--indoteknik_custom/views/account_move.xml8
-rw-r--r--indoteknik_custom/views/coretax_faktur.xml49
-rwxr-xr-xindoteknik_custom/views/product_template.xml12
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml49
-rw-r--r--indoteknik_custom/views/res_partner.xml108
-rw-r--r--indoteknik_custom/views/res_users.xml2
-rwxr-xr-xindoteknik_custom/views/sale_order.xml14
-rw-r--r--indoteknik_custom/views/stock_picking.xml61
-rw-r--r--indoteknik_custom/views/user_company_request.xml18
-rw-r--r--indoteknik_custom/views/user_pengajuan_tempo.xml344
-rw-r--r--indoteknik_custom/views/user_pengajuan_tempo_line.xml42
-rw-r--r--indoteknik_custom/views/user_pengajuan_tempo_request.xml416
50 files changed, 3790 insertions, 147 deletions
diff --git a/indoteknik_api/controllers/api_v1/flash_sale.py b/indoteknik_api/controllers/api_v1/flash_sale.py
index 00b1f2e0..6c4ad8c0 100644
--- a/indoteknik_api/controllers/api_v1/flash_sale.py
+++ b/indoteknik_api/controllers/api_v1/flash_sale.py
@@ -20,6 +20,7 @@ class FlashSale(controller.Controller):
query = [
('pricelist_id', '=', pricelist.id)
]
+ formatted_end_date = pricelist.end_date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' if pricelist.end_date else None
data.append({
'pricelist_id': pricelist.id,
'option': pricelist.flashsale_option,
@@ -29,6 +30,7 @@ class FlashSale(controller.Controller):
'banner_mobile': request.env['ir.attachment'].api_image('product.pricelist', 'banner_mobile', pricelist.id),
'banner_top': request.env['ir.attachment'].api_image('product.pricelist', 'banner_top', pricelist.id),
'duration': pricelist._remaining_time_in_second(),
+ 'end_date': formatted_end_date,
'product_total': request.env['product.pricelist.item'].search_count(query),
})
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/invoice.py b/indoteknik_api/controllers/api_v1/invoice.py
index 4937e8dd..4bd3746c 100644
--- a/indoteknik_api/controllers/api_v1/invoice.py
+++ b/indoteknik_api/controllers/api_v1/invoice.py
@@ -1,7 +1,7 @@
from .. import controller
from odoo import http
from odoo.http import request
-
+from odoo import fields
class Invoice(controller.Controller):
PREFIX = '/api/v1/'
@@ -15,6 +15,7 @@ class Invoice(controller.Controller):
'name': [],
'limit': ['default:0', 'number'],
'offset': ['default:0', 'number'],
+ 'status': [],
})
limit = params['value']['limit']
offset = params['value']['offset']
@@ -27,6 +28,11 @@ class Invoice(controller.Controller):
('state', '=', 'posted'),
('partner_id', 'in', partner_child_ids)
]
+ domain_orders = [
+ ('invoice_status', '!=', 'invoiced'),
+ ('state', '=', 'sale'),
+ ('partner_id', 'in', partner_child_ids)
+ ]
if params['value']['name']:
name = params['value']['name'].replace(' ', '%')
domain += [
@@ -34,10 +40,25 @@ class Invoice(controller.Controller):
('name', 'ilike', '%'+ name +'%'),
('ref', 'ilike', '%'+ name +'%')
]
+ if params['value']['status']:
+ if params['value']['status'] == '1':
+ domain += [('payment_state', '=', 'not_paid'), ('invoice_date_due', '<', fields.Date.today())]
+ elif params['value']['status'] == '2':
+ domain += [('payment_state', '=', 'not_paid'), ('invoice_date_due', '>', fields.Date.today())]
+ elif params['value']['status'] == '3':
+ domain += [('payment_state', '=', 'paid')]
+ elif params['value']['status'] == '0':
+ domain = domain
+ elif params['value']['status'] == '4':
+ domain += [('payment_state', '=', 'not_paid')]
+
invoices = request.env['account.move'].search(domain, offset=offset, limit=limit)
+ sale_orders = request.env['sale.order'].search(domain_orders, offset=offset, limit=limit)
data = {
'invoice_total': request.env['account.move'].search_count(domain),
- 'invoices': [request.env['account.move'].api_v1_single_response(x) for x in invoices]
+ 'invoices': [request.env['account.move'].api_v1_single_response(x) for x in invoices],
+ 'sale_order_total': request.env['sale.order'].search_count(domain_orders),
+ 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in sale_orders]
}
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index f9ec7101..307165b3 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -1,7 +1,10 @@
from .. import controller
from odoo import http
from odoo.http import request
-
+from odoo import fields
+import json
+import base64
+import mimetypes
class Partner(controller.Controller):
_name = 'res.partner'
@@ -135,6 +138,8 @@ class Partner(controller.Controller):
'npwp': [],
'alamat_lengkap_text': [],
'street': [],
+ 'email': [],
+ 'mobile': []
})
id_user = self.get_request_params(kw, {
'id_user': ['number']
@@ -213,6 +218,32 @@ class Partner(controller.Controller):
})
return self.response(data)
+
+ @http.route(prefix + 'partner/payment_term', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_partner_payment_term(self):
+ partner_industry = request.env['account.payment.term'].search([])
+ data = []
+ for industry in partner_industry:
+ if 'tempo' in industry.name.lower():
+ data.append({
+ 'id': industry.id,
+ 'name': industry.name
+ })
+
+ return self.response(data)
+
+ @http.route(prefix + 'partner/detail-tempo/<id>', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_partner_detail_tempo(self, **kw):
+ params = self.get_request_params(kw, {
+ 'id': ['required', 'number']
+ })
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', params['value']['id'])], limit=1)
+ if not pengajuan_tempo:
+ return self.response(code=404, description='pengajuan tempo not found')
+ pengajuan_tempo = request.env['res.partner'].api_single_response(pengajuan_tempo)
+ return self.response(pengajuan_tempo)
@http.route(prefix + 'check/<partner_id>/tempo', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
@@ -227,18 +258,318 @@ class Partner(controller.Controller):
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')]))
-
+
+ domain_result_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')]
+ domain_result_jatuh_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted'), ('invoice_date_due', '<', fields.Date.today())]
+ domain_orders = [('partner_id', '=', partner.id), ('invoice_status', '!=', 'invoiced'), ('state', '=', 'sale')]
+
+ result_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_tempo))
+ result_tempo_total = request.env['account.move'].search_count(domain_result_tempo)
+ result_jatuh_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_jatuh_tempo))
+ result_jatuh_tempo_total = request.env['account.move'].search_count(domain_result_jatuh_tempo)
+ orders = request.env['sale.order'].search(domain_orders)
+ orders_total = request.env['sale.order'].search_count(domain_orders)
+
+ total_amount = sum(order.amount_total for order in orders)
+
remaining_limit = partner.blocking_stage - result_tempo if partner.active_limit else None
data = {
'name': partner.name,
+ 'payment_term': partner.property_payment_term_id.name,
'amount_due': result_tempo,
+ 'amount_due_total': result_tempo_total,
+ 'amount_jatuh_tempo_total': result_jatuh_tempo_total,
+ 'amount_jatuh_tempo': result_jatuh_tempo,
+ 'amount_due_sale': total_amount,
+ 'amount_due_sale_total': orders_total,
'remaining_limit': remaining_limit
}
-
+
+ return self.response(data)
+
+ @http.route(prefix + 'check/<partner_id>/tempo_progress', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_check_tempo_partner_progres(self, **kw):
+ partner_id = int(kw.get('partner_id'))
+ partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1)
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', partner.id)], limit=1)
+ if not pengajuan_tempo:
+ return self.response(code=404, description='Partner not found')
+
+ data = True if pengajuan_tempo.id else False
return self.response(data)
+ @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST'], csrf=False)
+ @controller.Controller.must_authorized()
+ def write_pengajuan_tempo(self, **kw):
+ id = int(kw.get('partner_id'))
+ user_id = int(kw.get('user_id'))
+ tempo_request = True if kw.get('tempo_request') == 'true' else False
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', user_id)], limit=1)
+ user = request.env['res.partner'].search([('id', '=', user_id)], limit=1)
+ company_name = kw.get('name', pengajuan_tempo.name_tempo.name)
+ partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1)
+ user_account = self.get_user_by_email(user.email)
+ params = self.get_request_params(kw, {
+
+ # informasi perusahaan
+ # 'name': ['required', 'alias:name_tempo'],
+ 'industryId': ['alias:industry_id_tempo'],
+ 'street': ['alias:street_tempo'],
+ 'state': ['alias:state_id_tempo'],
+ 'city': ['alias:city_id_tempo'],
+ 'district': ['alias:district_id_tempo'],
+ 'subDistrict': ['alias:subDistrict_id_tempo'],
+ 'zip': ['alias:zip_tempo'],
+ 'mobile': ['alias:mobile_tempo'],
+ 'bankName': ['alias:bank_name_tempo'],
+ 'accountName': ['alias:account_name_tempo'],
+ 'accountNumber': ['alias:account_number_tempo'],
+ 'website': ['alias:website_tempo'],
+ 'estimasi': ['alias:estimasi_tempo'],
+ 'portal': ['alias:portal'],
+ 'bersedia': ['alias:bersedia'],
+ 'tempoDuration': ['alias:tempo_duration'],
+ 'tempoLimit': ['alias:tempo_limit'],
+
+ # informasi perusahaan
+ 'direkturTittle': ['alias:direktur_tittle'],
+ 'direkturName': ['alias:direktur_name'],
+ 'direkturMobile': ['alias:direktur_mobile'],
+ 'direkturEmail': ['alias:direktur_email'],
+ 'purchasingTittle': ['alias:purchasing_tittle'],
+ 'purchasingName': ['alias:purchasing_name'],
+ 'purchasingMobile': ['alias:purchasing_mobile'],
+ 'purchasingEmail': ['alias:purchasing_email'],
+ 'financeTittle': ['alias:finance_tittle'],
+ 'financeName': ['alias:finance_name'],
+ 'financeMobile': ['alias:finance_mobile'],
+ 'financeEmail': ['alias:finance_email'],
+
+ # Pengiriman
+ 'PICTittle': ['alias:pic_tittle'],
+ 'PICName': ['alias:pic_name'],
+ 'streetPengiriman': ['alias:street_pengiriman'],
+ 'statePengiriman': ['alias:state_id_pengiriman'],
+ 'cityPengiriman': ['alias:city_id_pengiriman'],
+ 'districtPengiriman': ['alias:district_id_pengiriman'],
+ 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'],
+ 'zipPengiriman': ['alias:zip_pengiriman'],
+ 'invoicePicTittle': ['alias:invoice_pic_tittle'],
+ 'invoicePic': ['alias:invoice_pic'],
+ 'streetInvoice': ['alias:street_invoice'],
+ 'stateInvoice': ['alias:state_id_invoice'],
+ 'cityInvoice': ['alias:city_id_invoice'],
+ 'districtInvoice': ['alias:district_id_invoice'],
+ 'subDistrictInvoice': ['alias:subDistrict_id_invoice'],
+ 'zipInvoice': ['alias:zip_invoice'],
+ 'isSameAddrees':['alias:is_same_address'],
+ 'isSameAddreesStreet':['alias:is_same_address_street'],
+ })
+
+ # # Konversi nilai 'true' ke boolean True
+ # is_same_address = kw.get('isSameAddrees', 'false').lower() == 'true'
+ # is_same_address_street = kw.get('isSameAddreesStreet', 'false').lower() == 'true'
+ #
+ # # Tambahkan nilai yang dikonversi ke params
+ # if 'isSameAddress' in kw:
+ # params['value']['is_same_address'] = is_same_address
+ # if 'is_same_address_street' in kw:
+ # params['value']['is_same_address_street'] = is_same_address_street
+
+ if not params['valid']:
+ return self.response(code=400, description=params)
+ if params['value']['portal']:
+ if params['value']['portal'] == 'ada':
+ params['value']['portal'] = True
+ else:
+ params['value']['portal'] = False
+ # Filter data baru yang dikirim (non-kosong, boolean False tetap masuk)
+ new_data = {key: value for key, value in params['value'].items() if value != ''}
+
+ if pengajuan_tempo:
+ # Jika pengajuan_tempo sudah ada, hanya write data baru yang non-kosong
+ pengajuan_tempo.write(new_data)
+ else:
+ # Jika belum ada, buat record baru
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data)
+ pengajuan_tempo.partner_id = user_id
+
+ if partner_id:
+ pengajuan_tempo.name_tempo = partner_id
+
+ form_supplier_data = kw.get('formSupplier', False)
+
+ if form_supplier_data:
+ try:
+ form_supplier_data = json.loads(form_supplier_data)
+
+ supplier_ids_to_add = []
+ for item in form_supplier_data:
+ supplier_name = item.get("supplier")
+ pic_name = item.get("pic")
+ phone = item.get("telepon")
+ tempo_duration = item.get("durasiTempo")
+ credit_limit = item.get("creditLimit")
+
+ new_data = {
+ 'name_supplier': supplier_name,
+ 'pic_name': pic_name,
+ 'phone': phone,
+ 'tempo_duration': tempo_duration,
+ 'credit_limit': credit_limit,
+ }
+ new_supplier_data = request.env['user.pengajuan.tempo.line'].create(new_data)
+
+ supplier_ids_to_add.append(new_supplier_data.id)
+
+ pengajuan_tempo.write({'supplier_ids': [(6, 0, supplier_ids_to_add)]})
+
+ except json.JSONDecodeError:
+ return http.Response(status=400, json_body={'error': 'Invalid JSON format for formSupplier'})
+ category_produk_ids = kw.get('categoryProduk', False)
+ category_ids = ''
+ if category_produk_ids:
+ category_ids = list(map(int, category_produk_ids.split(',')))
+ pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)]
+
+ tukar_invoice_input = kw.get('tukarInvoiceInput')
+ if tukar_invoice_input:
+ pengajuan_tempo.tukar_invoice = tukar_invoice_input
+
+ tukar_invoice_input_pembayaran = kw.get('tukarInvoiceInputPembayaran')
+ if tukar_invoice_input_pembayaran:
+ pengajuan_tempo.jadwal_bayar = tukar_invoice_input_pembayaran
+
+ dokumen_kirim = [
+ 'Surat Tanda Terima Barang (STTB)',
+ 'Good Receipt (GR)',
+ 'Surat Terima Barang (STB)',
+ 'Lembar Penerimaan Barang (LPB)'
+ ]
+
+ dokumen_kirim_barang_ids = kw.get('dokumenPengiriman')
+ dokumen_kirim_input = kw.get('dokumenKirimInput', '')
+ dokumen_kirim_barang_input = kw.get('dokumenPengirimanInput', '')
+ dokumen_kirim_barang = []
+
+ if dokumen_kirim_barang_ids:
+ dokumen_kirim_ids = list(map(int, dokumen_kirim_barang_ids.split(',')))
+ dokumen_kirim_barang = [dokumen_kirim[i] for i in dokumen_kirim_ids if 0 <= i < len(dokumen_kirim)]
+ if dokumen_kirim_input:
+ input_items = [item.strip() for item in dokumen_kirim_input.split(',')]
+ dokumen_kirim_barang.extend(item for item in input_items if item and item not in dokumen_kirim_barang)
+ pengajuan_tempo.dokumen_kirim_input = dokumen_kirim_input
+ if dokumen_kirim_barang:
+ pengajuan_tempo.dokumen_pengiriman = ', '.join(dokumen_kirim_barang)
+ if dokumen_kirim_barang_input:
+ pengajuan_tempo.dokumen_pengiriman_input = dokumen_kirim_barang_input
+
+ dokumen = [
+ 'Invoice Pembelian',
+ 'Surat Jalan',
+ 'Berita Acara Serah Terima (BAST)',
+ 'Faktur Pajak',
+ 'Good Receipt (GR)'
+ ]
+
+ dokumen_invoice = kw.get('dokumenPengirimanInvoice', '')
+ if dokumen_invoice:
+ pengajuan_tempo.dokumen_invoice = dokumen_invoice
+ user_tempo_request = []
+ if tempo_request:
+ user_tempo_request = request.env['user.pengajuan.tempo.request'].create({
+ 'user_id': id,
+ 'pengajuan_tempo_id': pengajuan_tempo.id,
+ 'user_company_id': partner_id.id,
+ 'tempo_duration': pengajuan_tempo.tempo_duration.id,
+ 'tempo_limit': pengajuan_tempo.tempo_limit,
+ })
+
+ form_dokumen_data = kw.get('formDocs', False)
+ if form_dokumen_data:
+ try:
+ form_dokumen = json.loads(form_dokumen_data)
+
+ for dokumen in form_dokumen:
+ if dokumen['details']['base64'] != '':
+ mimetype, _ = mimetypes.guess_type(dokumen['details']['name'])
+ mimetype = mimetype or 'application/octet-stream'
+ data = base64.b64decode(dokumen['details']['base64'])
+ sppkp_attachment = request.env['ir.attachment'].create({
+ 'name': dokumen['details']['name'],
+ 'type': 'binary',
+ 'datas': base64.b64encode(data),
+ 'res_model': 'user.pengajuan.tempo',
+ 'res_id': pengajuan_tempo.id,
+ 'mimetype': mimetype
+ })
+
+ if dokumen['documentName'] == 'dokumenNib':
+ pengajuan_tempo.dokumen_nib = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSiup':
+ pengajuan_tempo.dokumen_siup = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenTdp':
+ pengajuan_tempo.dokumen_tdp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSkdp':
+ pengajuan_tempo.dokumen_skdp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSkt':
+ pengajuan_tempo.dokumen_skt = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenNpwp':
+ pengajuan_tempo.dokumen_npwp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSppkp':
+ pengajuan_tempo.dokumen_sppkp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenAktaPerubahan':
+ pengajuan_tempo.dokumen_akta_perubahan = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenKtpDirut':
+ pengajuan_tempo.dokumen_ktp_dirut = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenAktaPendirian':
+ pengajuan_tempo.dokumen_akta_pendirian = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenLaporanKeuangan':
+ pengajuan_tempo.dokumen_laporan_keuangan = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenFotoKantor':
+ pengajuan_tempo.dokumen_foto_kantor = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenTempatBekerja':
+ pengajuan_tempo.dokumen_tempat_bekerja = [(6, 0, [sppkp_attachment.id])]
+
+ formatted_text = ''.join([' ' + char if char.isupper() and i != 0 else char for i, char in
+ enumerate(dokumen['documentName'])])
+ teks = formatted_text.strip().title()
+ pengajuan_tempo.message_post(body=teks, attachment_ids=[sppkp_attachment.id])
+ if tempo_request:
+ user_tempo_request.message_post(body=teks, attachment_ids=[sppkp_attachment.id])
+
+
+ except json.JSONDecodeError:
+ return http.Response(status=400, json_body={'error': 'Invalid JSON format for formDokumen'})
+
+ if tempo_request:
+ # pengajuan_tempo.user_id = id
+ template = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_request_tempo_review')
+ template.send_mail(pengajuan_tempo.id, force_send=True)
+ template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales')
+ template2.send_mail(pengajuan_tempo.id, force_send=True)
+ return self.response({
+ 'id': pengajuan_tempo.id,
+ 'user_id': user_id,
+ })
+ 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_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 3e182a2e..8b95ade8 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -150,7 +150,8 @@ class SaleOrder(controller.Controller):
def partner_checkout_sale_order_by_id(self, **kw):
params = self.get_request_params(kw, {
'partner_id': ['number'],
- 'id': ['number']
+ 'id': ['number'],
+ 'status': ['boolean']
})
if not params['valid']:
return self.response(code=400, description=params)
@@ -163,6 +164,9 @@ class SaleOrder(controller.Controller):
data = {}
sale_order = request.env['sale.order'].search(domain)
if sale_order:
+ if 'status' in params['value']:
+ sale_order.is_continue_transaction = params['value']['status']
+
if sale_order._requires_approval_margin_leader():
sale_order.approval_status = 'pengajuan2'
elif sale_order._requires_approval_margin_manager():
diff --git a/indoteknik_api/controllers/api_v1/state.py b/indoteknik_api/controllers/api_v1/state.py
index 598ef70b..958359a7 100644
--- a/indoteknik_api/controllers/api_v1/state.py
+++ b/indoteknik_api/controllers/api_v1/state.py
@@ -8,7 +8,11 @@ class District(controller.Controller):
@http.route(prefix + 'state', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_state(self, **kw):
+ tempo = kw.get('tempo')
parameters = []
+ if tempo == 'true':
+ parameters.append(('country_id', '=', 100))
+
name = kw.get('name')
if name:
@@ -18,7 +22,7 @@ class District(controller.Controller):
states = request.env['res.country.state'].search(parameters)
data = []
for state in states:
- data.append({ 'id': state.id, 'name': state.name })
+ data.append({ 'id': state.id, 'name': state.name})
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py
index a94ba1f8..f651d9fa 100644
--- a/indoteknik_api/controllers/api_v1/user.py
+++ b/indoteknik_api/controllers/api_v1/user.py
@@ -403,6 +403,20 @@ class User(controller.Controller):
'user': self.response_with_token(user)
})
+ @http.route(prefix + 'user/<id>/after_request_tempo', auth='public', methods=['PUT', 'OPTIONS'], csrf=False)
+ @controller.Controller.must_authorized()
+ def update_user_tempo_after_request(self, **kw):
+ id = kw.get('id')
+
+ user = request.env['res.users'].search([('id', '=', id)], limit=1)
+ if not user:
+ return self.response(code=404, description='User not found')
+
+
+ return self.response({
+ 'user': self.response_with_token(user)
+ })
+
@http.route(prefix + 'user/<id>/address', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_user_address_by_id(self, **kw):
diff --git a/indoteknik_api/controllers/controller.py b/indoteknik_api/controllers/controller.py
index a34a2688..80f45074 100644
--- a/indoteknik_api/controllers/controller.py
+++ b/indoteknik_api/controllers/controller.py
@@ -4,6 +4,7 @@ import functools
import io
import json
from array import array
+from io import BytesIO
import jwt
from odoo import http
@@ -11,6 +12,8 @@ from odoo.http import request
from odoo.modules import get_module_resource
from odoo.tools.config import config
from PIL import Image
+from PIL.WebPImagePlugin import Image
+from PIL import features
from pytz import timezone
@@ -204,6 +207,8 @@ class Controller(http.Controller):
if not variant:
image = self.add_watermark_to_image(image, ratio, version)
+ # image = self.convert_to_webp(image)
+
response_headers = [
('Content-Type', 'image/jpg'),
('Cache-Control', 'public, max-age=3600')
@@ -214,6 +219,31 @@ class Controller(http.Controller):
response_headers
)
+ def convert_to_webp(self, image_base64):
+ """Convert image from base64 to WebP format and return base64 WebP."""
+ try:
+ print(f"Image base64 length: {len(image_base64)}")
+
+ # Decode Base64 to Bytes
+ image_data = base64.b64decode(image_base64)
+ image = Image.open(BytesIO(image_data))
+
+ if image.format == "PNG" and image.mode != "RGBA":
+ image = image.convert("RGBA")
+
+ # Convert to WebP
+ with BytesIO() as output:
+ image.save(output, format="WEBP", quality=85)
+ webp_data = output.getvalue()
+
+ # Encode back to Base64
+ return base64.b64encode(webp_data).decode('utf-8')
+ except Exception as e:
+ print(f"Error details: {e}")
+ # If conversion fails, return the original image
+ request.env.cr.rollback() # Rollback any transactions
+ return image_base64
+
def add_watermark_to_image(self, image, ratio, version = '1'):
if not image: return ''
diff --git a/indoteknik_api/models/__init__.py b/indoteknik_api/models/__init__.py
index 892d2657..8c85938c 100644
--- a/indoteknik_api/models/__init__.py
+++ b/indoteknik_api/models/__init__.py
@@ -9,3 +9,4 @@ from . import sale_order
from . import x_manufactures
from . import website_content
from . import coupon_program
+from . import res_partner
diff --git a/indoteknik_api/models/account_move.py b/indoteknik_api/models/account_move.py
index 23a7076c..645c157a 100644
--- a/indoteknik_api/models/account_move.py
+++ b/indoteknik_api/models/account_move.py
@@ -7,6 +7,7 @@ class AccountMove(models.Model):
def api_v1_single_response(self, account_move, context=False):
sale_order = self.env['sale.order'].search([('name', '=', account_move.invoice_origin), ('state', '=', 'done')], limit=1)
+ sale_order_v2 = self.env['sale.order'].search([('name', '=', account_move.invoice_origin)],limit=1)
amount_residual = account_move.amount_residual
if sale_order.payment_status == 'settlement' or sale_order.payment_status == 'capture':
amount_residual = 0
@@ -21,6 +22,9 @@ class AccountMove(models.Model):
'amount_residual': amount_residual,
'invoice_date': account_move.invoice_date.strftime('%d/%m/%Y') or '',
'efaktur': True if account_move.efaktur_document else False,
+ 'invoice_date_due': account_move.invoice_date_due.strftime('%d/%m/%Y') or '-',
+ 'sales_order': account_move.invoice_origin,
+ 'sales_order_id': sale_order_v2.id,
}
if isinstance(object, datetime.date):
data['invoice_date'] = account_move.invoice_date.strftime('%d/%m/%Y')
@@ -35,7 +39,6 @@ class AccountMove(models.Model):
'sales': account_move.invoice_user_id.name,
'amount_total': account_move.amount_total,
'amount_residual': amount_residual,
- 'invoice_date_due': account_move.invoice_date_due.strftime('%d/%m/%Y') or '',
'customer': res_users.api_address_response(account_move.partner_id),
'products': [],
}
diff --git a/indoteknik_api/models/product_product.py b/indoteknik_api/models/product_product.py
index f8869c7d..10922186 100644
--- a/indoteknik_api/models/product_product.py
+++ b/indoteknik_api/models/product_product.py
@@ -139,39 +139,39 @@ class ProductProduct(models.Model):
return retValue
def _get_website_price_exclude_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_incl = self._get_website_price_include_tax()
res = price_incl / default_divide_tax
return math.floor(res)
def _v2_get_website_price_exclude_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_incl = self._v2_get_website_price_include_tax()
res = price_incl / default_divide_tax
return math.floor(res)
def _get_website_price_after_disc_and_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_after_disc = self._get_website_price_after_disc()
res = price_after_disc / default_divide_tax
res = math.ceil(res)
return res
def _v2_get_website_price_after_disc_and_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_after_disc = self._v2_get_website_price_after_disc()
res = price_after_disc / default_divide_tax
res = math.ceil(res)
return res
def _get_website_tax(self):
- default_percent_tax = float(11)
+ default_percent_tax = float(12)
price_after_disc = self._get_website_price_after_disc_and_tax()
res = price_after_disc * default_percent_tax / 100
return math.floor(res)
def _v2_get_website_tax(self):
- default_percent_tax = float(11)
+ default_percent_tax = float(12)
price_after_disc = self._v2_get_website_price_after_disc_and_tax()
res = price_after_disc * default_percent_tax / 100
return math.floor(res)
diff --git a/indoteknik_api/models/product_template.py b/indoteknik_api/models/product_template.py
index 75899624..e46e44d3 100644
--- a/indoteknik_api/models/product_template.py
+++ b/indoteknik_api/models/product_template.py
@@ -7,6 +7,11 @@ class ProductTemplate(models.Model):
def api_single_response(self, product_template, with_detail=''):
product_pricelist_default_discount_id = self.env['ir.config_parameter'].get_param('product.pricelist.default_discount_id')
product_pricelist_default_discount_id = int(product_pricelist_default_discount_id)
+ voucher = self.get_voucher_pastihemat(product_template.x_manufacture.id)
+ newVoucherPastiHemat = {"min_purchase": voucher.min_purchase_amount or 0,
+ "discount_type": voucher.discount_type or '',
+ "discount_amount": voucher.discount_amount or 0,
+ "max_discount": voucher.max_discount_amount or 0,}
data = {
'id': product_template.id,
'image': self.env['ir.attachment'].api_image('product.template', 'image_128', product_template.id),
@@ -18,6 +23,7 @@ class ProductTemplate(models.Model):
'weight': product_template.weight,
'manufacture': self.api_manufacture(product_template),
'categories': self.api_categories(product_template),
+ "newVoucherPastiHemat": newVoucherPastiHemat
}
if with_detail != '':
diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py
new file mode 100644
index 00000000..0e09fbc6
--- /dev/null
+++ b/indoteknik_api/models/res_partner.py
@@ -0,0 +1,170 @@
+from odoo import models
+import json
+import base64
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ def api_single_response(self, pengajuan_tempo, with_detail=''):
+ config = self.env['ir.config_parameter']
+
+ partner = pengajuan_tempo.partner_id
+
+ dokumen_kirim = [
+ ['Surat Tanda Terima Barang (STTB)', '0'],
+ ['Good Receipt (GR)', '1'],
+ ['Surat Terima Barang (STB)', '2'],
+ ['Lembar Penerimaan Barang (LPB)', '3']
+ ]
+ dokumen_pengiriman = []
+ if pengajuan_tempo.dokumen_pengiriman :
+ pengajuan_tempo_dokumen_pengiriman = pengajuan_tempo.dokumen_pengiriman
+ mapping_dokumen = {item[0]: item[1] for item in dokumen_kirim}
+ dokumen_pengiriman_list = [dokumen.strip() for dokumen in pengajuan_tempo_dokumen_pengiriman.split(',')]
+ dokumen_pengiriman = [mapping_dokumen.get(dokumen, '4') for dokumen in dokumen_pengiriman_list]
+ data = {
+ 'name' : pengajuan_tempo.name_tempo.name,
+ 'industry_id' : pengajuan_tempo.industry_id_tempo.id,
+ 'street' : pengajuan_tempo.street_tempo,
+ 'state' : pengajuan_tempo.state_id_tempo.id,
+ 'city' : pengajuan_tempo.city_id_tempo.id,
+ 'district' : pengajuan_tempo.district_id_tempo.id,
+ 'subDistrict' : pengajuan_tempo.subDistrict_id_tempo.id,
+ 'zip' : pengajuan_tempo.zip_tempo,
+ 'mobile' : pengajuan_tempo.mobile_tempo,
+ 'bank_name' : pengajuan_tempo.bank_name_tempo,
+ 'account_name' : pengajuan_tempo.account_name_tempo,
+ 'account_number' : pengajuan_tempo.account_number_tempo,
+ 'website' : pengajuan_tempo.website_tempo if pengajuan_tempo.website_tempo else '',
+ 'estimasi' : pengajuan_tempo.estimasi_tempo,
+ 'bersedia' : pengajuan_tempo.bersedia,
+ 'portal' : 'ada' if pengajuan_tempo.portal else 'tidak',
+ 'tempo_duration' : pengajuan_tempo.tempo_duration.id,
+ 'tempo_limit' : pengajuan_tempo.tempo_limit,
+ 'category_produk': ','.join([str(cat.id) for cat in pengajuan_tempo.category_produk_ids]) if pengajuan_tempo.category_produk_ids else '',
+
+ # Kontak Perusahaan
+ 'direktur_tittle': pengajuan_tempo.direktur_tittle if pengajuan_tempo.direktur_tittle else '',
+ 'direktur_name' : pengajuan_tempo.direktur_name if pengajuan_tempo.direktur_name else '',
+ 'direktur_mobile' : pengajuan_tempo.direktur_mobile if pengajuan_tempo.direktur_mobile else '',
+ 'direktur_email' : pengajuan_tempo.direktur_email if pengajuan_tempo.direktur_email else '',
+ 'purchasing_tittle': pengajuan_tempo.purchasing_tittle if pengajuan_tempo.purchasing_tittle else '',
+ 'purchasing_name' : pengajuan_tempo.purchasing_name if pengajuan_tempo.purchasing_name else '',
+ 'purchasing_mobile' : pengajuan_tempo.purchasing_mobile if pengajuan_tempo.purchasing_mobile else '',
+ 'purchasing_email' : pengajuan_tempo.purchasing_email if pengajuan_tempo.purchasing_email else '',
+ 'finance_tittle': pengajuan_tempo.finance_tittle if pengajuan_tempo.finance_tittle else '',
+ 'finance_name' : pengajuan_tempo.finance_name if pengajuan_tempo.finance_name else '',
+ 'finance_mobile' : pengajuan_tempo.finance_mobile if pengajuan_tempo.finance_mobile else '',
+ 'finance_email' : pengajuan_tempo.finance_email if pengajuan_tempo.finance_email else '',
+
+ # Pengiriman
+ 'PIC_tittle' : pengajuan_tempo.pic_tittle if pengajuan_tempo.pic_tittle else '',
+ 'PIC_name' : pengajuan_tempo.pic_name if pengajuan_tempo.pic_name else '',
+ 'street_pengiriman' : pengajuan_tempo.street_pengiriman if pengajuan_tempo.street_pengiriman else '',
+ 'state_pengiriman' : pengajuan_tempo.state_id_pengiriman.id if pengajuan_tempo.state_id_pengiriman else '',
+ 'city_pengiriman' : pengajuan_tempo.city_id_pengiriman.id if pengajuan_tempo.city_id_pengiriman else '',
+ 'district_pengiriman': pengajuan_tempo.district_id_pengiriman.id if pengajuan_tempo.district_id_pengiriman else '',
+ 'subDistrict_pengiriman': pengajuan_tempo.subDistrict_id_pengiriman.id if pengajuan_tempo.subDistrict_id_pengiriman else '',
+ 'zip_pengiriman' : pengajuan_tempo.zip_pengiriman if pengajuan_tempo.zip_pengiriman else '',
+ 'invoice_pic_tittle' : pengajuan_tempo.invoice_pic_tittle if pengajuan_tempo.invoice_pic_tittle else '',
+ 'invoice_pic' : pengajuan_tempo.invoice_pic if pengajuan_tempo.invoice_pic else '',
+ 'street_invoice' : pengajuan_tempo.street_invoice if pengajuan_tempo.street_invoice else '',
+ 'state_invoice' : pengajuan_tempo.state_id_invoice.id if pengajuan_tempo.state_id_invoice else '',
+ 'city_invoice' : pengajuan_tempo.city_id_invoice.id if pengajuan_tempo.city_id_invoice else '',
+ 'district_invoice': pengajuan_tempo.district_id_invoice.id if pengajuan_tempo.district_id_invoice else '',
+ 'subDistrict_invoice': pengajuan_tempo.subDistrict_id_invoice.id if pengajuan_tempo.subDistrict_id_invoice else '',
+ 'zip_invoice': pengajuan_tempo.zip_invoice if pengajuan_tempo.zip_invoice else '',
+ 'tukar_invoice_input' : pengajuan_tempo.tukar_invoice if pengajuan_tempo.tukar_invoice else '',
+ 'tukar_invoice_input_pembayaran' : pengajuan_tempo.jadwal_bayar if pengajuan_tempo.jadwal_bayar else '',
+ 'dokumen_pengiriman' : ','.join(dokumen_pengiriman) if dokumen_pengiriman else '',
+ 'dokumen_pengiriman_input' : pengajuan_tempo.dokumen_pengiriman_input if pengajuan_tempo.dokumen_pengiriman_input else '',
+ 'dokumen_kirim_input' : pengajuan_tempo.dokumen_kirim_input if pengajuan_tempo.dokumen_kirim_input else '',
+ 'dokumen_pengiriman_invoice' : pengajuan_tempo.dokumen_invoice if pengajuan_tempo.dokumen_invoice else '',
+ 'is_same_addrees': pengajuan_tempo.is_same_address if pengajuan_tempo.is_same_address else False,
+ 'is_same_addrees_street': pengajuan_tempo.is_same_address_street if pengajuan_tempo.is_same_address_street else False,
+ 'supplier_ids': [
+ {
+ 'id': supplier.id,
+ 'supplier': supplier.name_supplier,
+ 'telepon': supplier.phone,
+ 'pic': supplier.pic_name,
+ 'credit_limit': supplier.credit_limit,
+ 'durasi_tempo': supplier.tempo_duration
+ }
+ for supplier in pengajuan_tempo.supplier_ids
+ ] if pengajuan_tempo.supplier_ids else '',
+ # Dokumen
+ 'dokumen_npwp':
+ {
+ 'name': pengajuan_tempo.dokumen_npwp.name,
+ 'base64': pengajuan_tempo.dokumen_npwp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_npwp.mimetype,
+ } if pengajuan_tempo.dokumen_npwp else '',
+ 'dokumen_sppkp': {
+ 'name': pengajuan_tempo.dokumen_sppkp.name,
+ 'base64': pengajuan_tempo.dokumen_sppkp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_sppkp.mimetype,
+ } if pengajuan_tempo.dokumen_sppkp else '',
+ 'dokumen_nib':
+ {
+ 'name': pengajuan_tempo.dokumen_nib.name,
+ 'base64': pengajuan_tempo.dokumen_nib.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_nib.mimetype,
+ }if pengajuan_tempo.dokumen_nib else '',
+ 'dokumen_siup':
+ {
+ 'name': pengajuan_tempo.dokumen_siup.name,
+ 'base64': pengajuan_tempo.dokumen_siup.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_siup.mimetype,
+ }if pengajuan_tempo.dokumen_siup else '',
+ 'dokumen_tdp':
+ {
+ 'name': pengajuan_tempo.dokumen_tdp.name,
+ 'base64': pengajuan_tempo.dokumen_tdp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_tdp.mimetype,
+ }if pengajuan_tempo.dokumen_tdp else '',
+ 'dokumen_skdp':
+ {
+ 'name': pengajuan_tempo.dokumen_skdp.name,
+ 'base64': pengajuan_tempo.dokumen_skdp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_skdp.mimetype,
+ }if pengajuan_tempo.dokumen_skdp else '',
+ 'dokumen_skt':
+ {
+ 'name': pengajuan_tempo.dokumen_skt.name,
+ 'base64': pengajuan_tempo.dokumen_skt.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_skt.mimetype,
+ }if pengajuan_tempo.dokumen_skt else '',
+ 'dokumen_akta_perubahan': {
+ 'name': pengajuan_tempo.dokumen_akta_perubahan.name,
+ 'base64': pengajuan_tempo.dokumen_akta_perubahan.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_akta_perubahan.mimetype,
+ } if pengajuan_tempo.dokumen_akta_perubahan else '',
+ 'dokumen_ktp_dirut': {
+ 'name': pengajuan_tempo.dokumen_ktp_dirut.name,
+ 'base64': pengajuan_tempo.dokumen_ktp_dirut.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_ktp_dirut.mimetype,
+ } if pengajuan_tempo.dokumen_ktp_dirut else '',
+ 'dokumen_akta_pendirian': {
+ 'name': pengajuan_tempo.dokumen_akta_pendirian.name,
+ 'base64': pengajuan_tempo.dokumen_akta_pendirian.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_akta_pendirian.mimetype,
+ } if pengajuan_tempo.dokumen_akta_pendirian else '',
+ 'dokumen_laporan_keuangan': {
+ 'name': pengajuan_tempo.dokumen_laporan_keuangan.name,
+ 'base64': pengajuan_tempo.dokumen_laporan_keuangan.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_laporan_keuangan.mimetype,
+ } if pengajuan_tempo.dokumen_laporan_keuangan else '',
+ 'dokumen_foto_kantor': {
+ 'name': pengajuan_tempo.dokumen_foto_kantor.name,
+ 'base64': pengajuan_tempo.dokumen_foto_kantor.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_foto_kantor.mimetype,
+ } if pengajuan_tempo.dokumen_foto_kantor else '',
+ 'dokumen_tempat_bekerja': {
+ 'name': pengajuan_tempo.dokumen_tempat_bekerja.name,
+ 'base64': pengajuan_tempo.dokumen_tempat_bekerja.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_tempat_bekerja.mimetype,
+ } if pengajuan_tempo.dokumen_tempat_bekerja else '',
+ }
+
+ return data \ No newline at end of file
diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py
index 1691acde..713bf50c 100644
--- a/indoteknik_api/models/res_users.py
+++ b/indoteknik_api/models/res_users.py
@@ -1,5 +1,5 @@
from odoo import models
-
+from odoo.http import request
class ResUsers(models.Model):
_inherit = 'res.users'
@@ -14,6 +14,15 @@ class ResUsers(models.Model):
'manager': 2,
'director': 3
}
+ partner_tempo = False
+ is_tempo_request = request.env['user.pengajuan.tempo.request'].search([('user_company_id', '=', main_partner.id)], limit=1)
+ tempo_progres = (
+ 'review' if is_tempo_request.state_tempo in ('draft', 'approval_sales', 'approval_finance') else
+ 'rejected' if is_tempo_request.state_tempo == 'reject' else
+ 'approve' if is_tempo_request.state_tempo == 'approval_director' else ''
+ )
+ if main_partner:
+ partner_tempo = True if 'tempo' in main_partner.get_check_payment_term().lower() else False
data = {
'id': res_user.id,
@@ -32,7 +41,9 @@ class ResUsers(models.Model):
'feature': {
'so_approval': main_partner.use_so_approval,
'only_ready_stock': main_partner.use_only_ready_stock
- }
+ },
+ 'partner_tempo': partner_tempo,
+ 'tempo_progres': tempo_progres
}
return data
@@ -70,7 +81,7 @@ class ResUsers(models.Model):
data['state_id'] = {
'id': user.state_id.id,
'name': user.state_id.name
- } or None
+ } or 0
if user.kota_id:
data['city'] = {
diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py
index 54e1fd40..727379c5 100644
--- a/indoteknik_api/models/sale_order.py
+++ b/indoteknik_api/models/sale_order.py
@@ -39,6 +39,8 @@ class SaleOrder(models.Model):
data['status'] = 'cancel'
if sale_order.state in ['draft', 'sent']:
data['status'] = 'draft'
+ if sale_order.is_continue_transaction:
+ data['status'] = 'waiting'
if sale_order.approval_status in ['pengajuan1', 'pengajuan2']:
data['status'] = 'waiting'
if sale_order.state == 'sale':
@@ -85,6 +87,11 @@ class SaleOrder(models.Model):
}
product['quantity'] = line.product_uom_qty
product['available_quantity'] = line.product_available_quantity
+ for data_v2 in sale_order.fulfillment_line_v2:
+ product_v2 = self.env['product.product'].api_single_response(data_v2.product_id)
+ if product['id'] == product_v2['id']:
+ product['so_qty'] = data_v2.so_qty
+ product['reserved_stock_qty'] = data_v2.reserved_stock_qty
data_with_detail['products'].append(product)
for invoice in sale_order.invoice_ids:
if invoice.state == 'posted':
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 89f62524..d44f85db 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -8,7 +8,7 @@
'author': 'Rafi Zadanly',
'website': '',
'images': ['assets/favicon.ico'],
- 'depends': ['base', 'coupon', 'delivery', 'sale', 'sale_management', 'vit_kelurahan'],
+ 'depends': ['base', 'coupon', 'delivery', 'sale', 'sale_management', 'vit_kelurahan', 'vit_efaktur' ],
'data': [
'security/ir.model.access.csv',
'views/group_partner.xml',
@@ -30,6 +30,7 @@
'views/web_logging/user_activity_log.xml',
'views/web_logging/web_utm_source.xml',
'views/user_company_request.xml',
+ 'views/user_pengajuan_tempo_request.xml',
'views/vit_kelurahan.xml',
'views/vit_kecamatan.xml',
'views/vit_kota.xml',
@@ -148,6 +149,7 @@
'views/approval_retur_picking.xml',
'views/form_vendor_approval_multi_approve.xml',
'views/form_vendor_approval_multi_reject.xml',
+ 'views/user_pengajuan_tempo.xml',
'report/report.xml',
'report/report_banner_banner.xml',
'report/report_banner_banner2.xml',
@@ -155,6 +157,7 @@
'report/report_invoice.xml',
'report/report_picking.xml',
'report/report_sale_order.xml',
+ 'views/coretax_faktur.xml',
],
'demo': [],
'css': [],
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index ad6d75dd..3990e81c 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -34,6 +34,7 @@ from . import stock_picking_type
from . import stock_picking
from . import stock_vendor
from . import user_company_request
+from . import user_pengajuan_tempo_request
from . import users
from . import website_brand_homepage
from . import website_categories_homepage
@@ -131,6 +132,10 @@ from . import approval_unreserve
from . import vendor_approval
from . import partner
from . import find_page
+from . import user_pengajuan_tempo_line
+from . import user_pengajuan_tempo
from . import approval_retur_picking
from . import va_multi_approve
from . import va_multi_reject
+from . import stock_immediate_transfer
+from . import coretax_fatur
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 725b3c2d..66726c8d 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -253,6 +253,11 @@ class AccountMove(models.Model):
line.date_maturity = entry.date
return res
+ def button_draft(self):
+ res = super(AccountMove, self).button_draft()
+ if not self.env.user.is_accounting:
+ raise UserError("Hanya Finence yang bisa ubah data")
+ return res
def _compute_invoice_day_to_due(self):
for invoice in self:
@@ -325,3 +330,35 @@ class AccountMove(models.Model):
# if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
# raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock')
# return res
+
+ def validate_faktur_for_export(self):
+ invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and
+ inv.state == 'posted' and
+ inv.move_type == 'out_invoice')
+
+ invalid_invoices = self - invoices
+ if invalid_invoices:
+ invalid_ids = ", ".join(str(inv.id) for inv in invalid_invoices)
+ raise UserError((
+ "Faktur dengan ID berikut tidak valid untuk diekspor: {}.\n"
+ "Pastikan faktur dalam status 'posted', belum diekspor, dan merupakan 'out_invoice'.".format(invalid_ids)
+ ))
+
+ return invoices
+
+ def export_faktur_to_xml(self):
+
+ valid_invoices = self.validate_faktur_for_export()
+
+ # Panggil model coretax.faktur untuk menghasilkan XML
+ coretax_faktur = self.env['coretax.faktur'].create({})
+ response = coretax_faktur.export_to_download(invoices=valid_invoices)
+
+ current_time = datetime.utcnow()
+ # Tandai faktur sebagai sudah diekspor
+ valid_invoices.write({
+ 'is_efaktur_exported': True,
+ 'date_efaktur_exported': current_time, # Set tanggal ekspor
+ })
+
+ return response
diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py
new file mode 100644
index 00000000..ae6dd2ae
--- /dev/null
+++ b/indoteknik_custom/models/coretax_fatur.py
@@ -0,0 +1,121 @@
+from odoo import models, fields
+import xml.etree.ElementTree as ET
+from xml.dom import minidom
+import base64
+import re
+
+class CoretaxFaktur(models.Model):
+ _name = 'coretax.faktur'
+ _description = 'Export Faktur ke XML'
+
+ export_file = fields.Binary(string="Export File", )
+ export_filename = fields.Char(string="Export File", )
+
+ def validate_and_format_number(slef, input_number):
+ # Hapus semua karakter non-digit
+ cleaned_number = re.sub(r'\D', '', input_number)
+
+ total_sum = sum(int(char) for char in cleaned_number)
+ if total_sum == 0 :
+ return '0000000000000000'
+
+ # Hitung jumlah digit
+ digit_count = len(cleaned_number)
+
+ # Jika jumlah digit kurang dari 15, tambahkan nol di depan
+ if digit_count < 16:
+ cleaned_number = cleaned_number.zfill(16)
+
+ return cleaned_number
+
+ def generate_xml(self, invoices=None):
+ # Buat root XML
+ root = ET.Element('TaxInvoiceBulk', {
+ 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
+ 'xsi:noNamespaceSchemaLocation': "TaxInvoice.xsd"
+ })
+ ET.SubElement(root, 'TIN').text = '0742260227086000'
+
+ # Tambahkan elemen ListOfTaxInvoice
+ list_of_tax_invoice = ET.SubElement(root, 'ListOfTaxInvoice')
+
+ # Dapatkan data faktur
+ # inv_obj = self.env['account.move']
+ # invoices = inv_obj.search([('is_efaktur_exported','=',True),
+ # ('state','=','posted'),
+ # ('efaktur_id','!=', False),
+ # ('move_type','=','out_invoice')], limit = 5)
+
+ for invoice in invoices:
+ tax_invoice = ET.SubElement(list_of_tax_invoice, 'TaxInvoice')
+ buyerTIN = self.validate_and_format_number(invoice.partner_id.npwp)
+ nitku = invoice.partner_id.nitku
+ formula = nitku if nitku else buyerTIN.ljust(len(buyerTIN) + 6, '0')
+ buyerIDTKU = formula if sum(int(char) for char in buyerTIN) > 0 else '000000'
+
+ # Tambahkan elemen faktur
+ ET.SubElement(tax_invoice, 'TaxInvoiceDate').text = invoice.invoice_date.strftime('%Y-%m-%d') if invoice.invoice_date else ''
+ ET.SubElement(tax_invoice, 'TaxInvoiceOpt').text = 'Normal'
+ ET.SubElement(tax_invoice, 'TrxCode').text = '04'
+ ET.SubElement(tax_invoice, 'AddInfo')
+ ET.SubElement(tax_invoice, 'CustomDoc')
+ ET.SubElement(tax_invoice, 'RefDesc').text = invoice.name
+ ET.SubElement(tax_invoice, 'FacilityStamp')
+ ET.SubElement(tax_invoice, 'SellerIDTKU').text = '0742260227086000000000'
+ ET.SubElement(tax_invoice, 'BuyerTin').text = buyerTIN
+ ET.SubElement(tax_invoice, 'BuyerDocument').text = 'TIN' if sum(int(char) for char in buyerTIN) > 0 else 'Other ID'
+ ET.SubElement(tax_invoice, 'BuyerCountry').text = 'IDN'
+ ET.SubElement(tax_invoice, 'BuyerDocumentNumber').text = '-' if sum(int(char) for char in buyerTIN) > 0 else str(invoice.partner_id.id)
+ ET.SubElement(tax_invoice, 'BuyerName').text = invoice.partner_id.nama_wajib_pajak or ''
+ ET.SubElement(tax_invoice, 'BuyerAdress').text = invoice.partner_id.alamat_lengkap_text or ''
+ ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or ''
+ ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU
+
+ # Tambahkan elemen ListOfGoodService
+ list_of_good_service = ET.SubElement(tax_invoice, 'ListOfGoodService')
+ for line in invoice.invoice_line_ids:
+ otherTaxBase = round(line.price_subtotal * (11/12)) if line.price_subtotal else 0
+ good_service = ET.SubElement(list_of_good_service, 'GoodService')
+ ET.SubElement(good_service, 'Opt').text = 'A'
+ ET.SubElement(good_service, 'Code')
+ ET.SubElement(good_service, 'Name').text = line.name
+ ET.SubElement(good_service, 'Unit').text = 'UM.0018'
+ ET.SubElement(good_service, 'Price').text = str(round(line.price_subtotal/line.quantity, 2)) if line.price_subtotal else '0'
+ ET.SubElement(good_service, 'Qty').text = str(line.quantity)
+ ET.SubElement(good_service, 'TotalDiscount').text = '0'
+ ET.SubElement(good_service, 'TaxBase').text = str(round(line.price_subtotal)) if line.price_subtotal else '0'
+ ET.SubElement(good_service, 'OtherTaxBase').text = str(otherTaxBase)
+ ET.SubElement(good_service, 'VATRate').text = '12'
+ ET.SubElement(good_service, 'VAT').text = str(round(otherTaxBase * 0.12, 2))
+ ET.SubElement(good_service, 'STLGRate').text = '0'
+ ET.SubElement(good_service, 'STLG').text = '0'
+
+ # Pretty print XML
+ xml_str = ET.tostring(root, encoding='utf-8')
+ xml_pretty = minidom.parseString(xml_str).toprettyxml(indent=" ")
+ return xml_pretty
+
+ def export_to_download(self, invoices):
+ # Generate XML content
+ xml_content = self.generate_xml(invoices)
+
+ # Encode content to Base64
+ xml_encoded = base64.b64encode(xml_content.encode('utf-8'))
+
+ # Buat attachment untuk XML
+ attachment = self.env['ir.attachment'].create({
+ 'name': 'Faktur_XML.xml',
+ 'type': 'binary',
+ 'datas': xml_encoded,
+ 'mimetype': 'application/xml',
+ 'store_fname': 'faktur_xml.xml',
+ })
+
+ # Kembalikan URL untuk download dengan header 'Content-Disposition'
+ response = {
+ 'type': 'ir.actions.act_url',
+ 'url': '/web/content/{}?download=true'.format(attachment.id),
+ 'target': 'self',
+ }
+
+ return response
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index 25473ab8..5bedae13 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -1,11 +1,13 @@
-from odoo import fields, models, api
+from odoo import fields, models, api, tools, _
from datetime import datetime, timedelta, date
from odoo.exceptions import UserError
import logging
import requests
import json
import re
+import qrcode, base64
from bs4 import BeautifulSoup
+from io import BytesIO
_logger = logging.getLogger(__name__)
@@ -58,14 +60,35 @@ class ProductTemplate(models.Model):
('sp', 'Spare Part'),
('acc', 'Accessories')
], string='Kind of', copy=False)
- sni = fields.Boolean(string='SNI')
+ sni = fields.Boolean(string='SNI')
tkdn = fields.Boolean(string='TKDN')
short_spesification = fields.Char(string='Short Spesification')
+ merchandise_ok = fields.Boolean(string='Product Promotion')
+ print_barcode = fields.Boolean(string='Print Barcode', default=True)
+ # qr_code = fields.Binary("QR Code", compute='_compute_qr_code')
+
+ # def _compute_qr_code(self):
+ # for rec in self.product_variant_ids:
+ # qr = qrcode.QRCode(
+ # version=1,
+ # error_correction=qrcode.constants.ERROR_CORRECT_L,
+ # box_size=5,
+ # border=4,
+ # )
+ # qr.add_data(rec.display_name)
+ # qr.make(fit=True)
+ # img = qr.make_image(fill_color="black", back_color="white")
+
+ # buffer = BytesIO()
+ # img.save(buffer, format="PNG")
+ # qr_code_img = base64.b64encode(buffer.getvalue()).decode()
+
+ # rec.qr_code = qr_code_img
@api.constrains('name', 'internal_reference', 'x_manufacture')
def required_public_categ_ids(self):
for rec in self:
- if not rec.public_categ_ids:
+ if not rec.public_categ_ids and rec.type == 'product':
raise UserError('Field Categories harus diisi')
def _get_qty_sold(self):
@@ -377,6 +400,31 @@ class ProductProduct(models.Model):
max_qty_reorder = fields.Float(string='Max Qty Reorder', compute='_get_max_qty_reordering_rule')
qty_rpo = fields.Float(string='Qty RPO', compute='_get_qty_rpo')
plafon_qty = fields.Float(string='Max Plafon', compute='_get_plafon_qty_product')
+ merchandise_ok = fields.Boolean(string='Product Promotion')
+ qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant')
+
+ def _compute_qr_code_variant(self):
+ for rec in self:
+ # Skip inactive variants
+ if not rec.active:
+ rec.qr_code_variant = False # Clear the QR Code for archived variants
+ continue
+
+ qr = qrcode.QRCode(
+ version=1,
+ error_correction=qrcode.constants.ERROR_CORRECT_L,
+ box_size=5,
+ border=4,
+ )
+ qr.add_data(rec.default_code)
+ qr.make(fit=True)
+ img = qr.make_image(fill_color="black", back_color="white")
+
+ buffer = BytesIO()
+ img.save(buffer, format="PNG")
+ qr_code_img = base64.b64encode(buffer.getvalue()).decode()
+
+ rec.qr_code_variant = qr_code_img
def _get_clean_website_description(self):
for rec in self:
@@ -386,7 +434,7 @@ class ProductProduct(models.Model):
@api.constrains('name', 'internal_reference', 'x_manufacture')
def required_public_categ_ids(self):
for rec in self:
- if not rec.public_categ_ids:
+ if not rec.public_categ_ids and rec.type == 'product':
raise UserError('Field Categories harus diisi')
@api.constrains('active')
@@ -482,33 +530,38 @@ class ProductProduct(models.Model):
def _get_qty_incoming_bandengan(self):
for product in self:
- qty_incoming = self.env['stock.move'].search([
- ('product_id', '=', product.id),
- ('location_dest_id', 'in', [57, 83]),
- ('state', 'not in', ['done', 'cancel'])
- ])
- qty = sum(qty_incoming.mapped('product_uom_qty'))
+ qty = self.env['v.move.outstanding'].read_group(
+ domain=[
+ ('product_id', '=', product.id),
+ ('location_dest_id', 'in', [57, 83]),
+ ],
+ fields=['qty_need'],
+ groupby=[]
+ )[0].get('qty_need', 0.0)
product.qty_incoming_bandengan = qty
def _get_qty_incoming_bandengan_with_exclude(self):
for product in self:
- qty_incoming = self.env['stock.move'].search([
- ('product_id', '=', product.id),
- ('location_dest_id', 'in', [57, 83]),
- ('state', 'not in', ['done', 'cancel'])
- ])
- qty = sum(qty_incoming.mapped('product_uom_qty'))
+ qty = self.env['v.move.outstanding'].read_group(
+ domain=[
+ ('product_id', '=', product.id),
+ ('location_dest_id', 'in', [57, 83]),
+ ],
+ fields=['qty_need'],
+ groupby=[]
+ )[0].get('qty_need', 0.0)
product.qty_incoming_bandengan = qty
def _get_qty_outgoing_bandengan(self):
for product in self:
- qty_incoming = self.env['stock.move'].search([
- ('product_id', '=', product.id),
- ('location_dest_id', '=', 5),
- ('location_id', 'in', [57, 83]),
- ('state', 'not in', ['done', 'cancel'])
- ])
- qty = sum(qty_incoming.mapped('product_uom_qty'))
+ qty = self.env['v.move.outstanding'].read_group(
+ domain=[
+ ('product_id', '=', product.id),
+ ('location_id', 'in', [57, 83]),
+ ],
+ fields=['qty_need'],
+ groupby=[]
+ )[0].get('qty_need', 0.0)
product.qty_outgoing_bandengan = qty
def _get_qty_onhand_bandengan(self):
@@ -598,3 +651,34 @@ class ProductProduct(models.Model):
('end_date', '>=', current_time)
], limit=1)
return pricelist
+
+
+class OutstandingMove(models.Model):
+ _name = 'v.move.outstanding'
+ _auto = False
+ _rec_name = 'id'
+
+ id = fields.Integer(string='ID')
+ product_id = fields.Many2one('product.product', string='Product')
+ reference = fields.Char(string='Reference', help='Nomor Dokumen terkait')
+ qty_need = fields.Float(string='Qty Need', help='Qty yang akan outgoing / incoming')
+ location_id = fields.Many2one('stock.location', string='Location', help='Lokasi asal')
+ location_dest_id = fields.Many2one('stock.location', string='Location To', help='Lokasi tujuan')
+
+ def init(self):
+ # where clause 'state in' follow the origin of outgoing and incoming odoo
+ tools.drop_view_if_exists(self.env.cr, self._table)
+ self.env.cr.execute("""
+ CREATE OR REPLACE VIEW %s AS
+ select sm.id, sm.reference, sm.product_id,
+ sm.product_uom_qty as qty_need,
+ sm.location_id, sm.location_dest_id
+ from stock_move sm
+ where 1=1
+ and sm.state in(
+ 'waiting',
+ 'confirmed',
+ 'assigned',
+ 'partially_available'
+ )
+ """ % self._table)
diff --git a/indoteknik_custom/models/promotion/sale_order.py b/indoteknik_custom/models/promotion/sale_order.py
index be820c6f..1c31d060 100644
--- a/indoteknik_custom/models/promotion/sale_order.py
+++ b/indoteknik_custom/models/promotion/sale_order.py
@@ -10,11 +10,12 @@ class SaleOrder(models.Model):
for promotion in promotions:
program_line = self.env['promotion.program.line'].browse(promotion['program_line_id'])
for free_product in program_line.free_product_ids:
- self.env['sale.order.line'].create({
- 'order_id': self.id,
- 'name': "Free Product " + free_product.product_id.display_name,
- 'display_type': 'line_note'
- })
+ if free_product.product_id.merchandise_ok:
+ self.env['sale.order.line'].create({
+ 'order_id': self.id,
+ 'name': f"Free Product {free_product.product_id.display_name} Quantity ({free_product.qty})",
+ 'display_type': 'line_note'
+ })
def apply_promotion_program(self):
userdata = {
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 9388ae4c..a69bfc15 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -30,6 +30,11 @@ class PurchaseOrder(models.Model):
('pengajuan2', 'Approval Pimpinan'), #akbar - 7 temporary not used
('approved', 'Approved'),
], string='Approval Status', readonly=True, copy=False, index=True, tracking=3)
+ approval_status_unlock = fields.Selection([
+ ('pengajuanFinance', 'Pengajuan Finance'),
+ ('approvedFinance', 'Approved Finance'),
+ ('approved', 'Approved'),
+ ], string='Approval Status Unlock', 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(
@@ -74,7 +79,41 @@ class PurchaseOrder(models.Model):
approve_by = fields.Many2one('res.users', string='Approve By')
exclude_incoming = fields.Boolean(string='Exclude Incoming', default=False,
help='Centang jika tidak mau masuk perhitungan Incoming Qty')
+ not_update_purchasepricelist = fields.Boolean(string='Not Update Purchase Pricelist?')
+ reason_unlock = fields.Char(string='Alasan unlock', tracking=3)
+ # total_cost_service = fields.Float(string='Total Cost Service' )
+ # total_delivery_amt = fields.Float(string='Total Delivery Amt')
+ total_cost_service = fields.Float(string='Total Cost Service')
+ total_delivery_amt = fields.Float(string='Total Delivery Amt')
+ store_name = fields.Char(string='Nama Toko')
+
+ @api.onchange('total_cost_service')
+ def _onchange_total_cost_service(self):
+ for order in self:
+ lines = order.order_line
+ if order.total_cost_service > 0:
+ if lines:
+ # Hitung nilai rata-rata cost_service
+ per_line_cost_service = order.total_cost_service / len(lines)
+ for line in lines:
+ line.cost_service = per_line_cost_service
+ else:
+ for line in lines:
+ line.cost_service = 0
+ @api.onchange('total_delivery_amt')
+ def _onchange_total_delivery_amt(self):
+ for order in self:
+ lines = order.order_line
+ if order.total_delivery_amt > 0:
+ if lines:
+ # Hitung nilai rata-rata delivery_amt
+ per_line_delivery_amt = order.total_delivery_amt / len(lines)
+ for line in lines:
+ line.delivery_amt = per_line_delivery_amt
+ else:
+ for line in lines:
+ line.delivery_amt = 0
def _compute_total_margin_match(self):
for purchase in self:
match = self.env['purchase.order.sales.match']
@@ -114,6 +153,7 @@ class PurchaseOrder(models.Model):
'ref': self.name,
'invoice_date': current_date,
'date': current_date,
+ 'invoice_origin': self.name,
'move_type': 'in_invoice'
}
@@ -164,6 +204,11 @@ class PurchaseOrder(models.Model):
self.bills_pelunasan_id = bills.id
+ lognote_message = (
+ f"Vendor bill created from: {self.name} ({self.partner_ref})"
+ )
+ bills.message_post(body=lognote_message)
+
return {
'name': _('Account Move'),
'view_mode': 'tree,form',
@@ -173,12 +218,10 @@ class PurchaseOrder(models.Model):
'domain': [('id', '=', bills.id)]
}
-
-
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,
@@ -186,8 +229,8 @@ class PurchaseOrder(models.Model):
'ref': self.name,
'invoice_date': current_date,
'date': current_date,
+ 'invoice_origin': self.name,
'move_type': 'in_invoice'
-
}
bills = self.env['account.move'].create([data_bills])
@@ -196,14 +239,13 @@ class PurchaseOrder(models.Model):
data_line_bills = {
'move_id': bills.id,
- 'product_id': product_dp.id, #product down payment
- 'account_id': 401, #Uang Muka persediaan barang dagang
+ '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
@@ -212,6 +254,12 @@ class PurchaseOrder(models.Model):
move_line.name = '[IT.121456] Down Payment'
move_line.partner_id = self.partner_id.id
+ # Tambahkan lognote
+ lognote_message = (
+ f"Vendor bill created from: {self.name} ({self.partner_ref})"
+ )
+ bills.message_post(body=lognote_message)
+
return {
'name': _('Account Move'),
'view_mode': 'tree,form',
@@ -221,6 +269,7 @@ class PurchaseOrder(models.Model):
'domain': [('id', '=', bills.id)]
}
+
def get_date_done(self):
picking = self.env['stock.picking'].search([
('purchase_id', '=', self.id),
@@ -620,7 +669,8 @@ class PurchaseOrder(models.Model):
raise UserError("Tidak ada link dengan SO, harus approval Merchandise")
send_email = False
- self.add_product_to_pricelist()
+ if not self.not_update_purchasepricelist:
+ self.add_product_to_pricelist()
for line in self.order_line:
if not line.product_id.purchase_ok:
raise UserError("Terdapat barang yang tidak bisa diproses")
@@ -747,8 +797,12 @@ class PurchaseOrder(models.Model):
def po_approve(self):
greater_than_plafon, message = self._get_msg_plafon_qty()
different_vendor_message = self.check_different_vendor_so() # Panggil fungsi check_different_vendor_so
-
- if self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
+ if self.approval_status_unlock == 'pengajuanFinance':
+ if self.env.user.is_accounting:
+ self.approval_status_unlock = 'approvedFinance'
+ else:
+ raise UserError("Bisa langsung Confirm, menunggu persetujuan Finance jika ingin unlock PO")
+ elif self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
raise UserError("Bisa langsung Confirm")
elif self.total_percent_margin == self.total_so_percent_margin and self.matches_so and not greater_than_plafon and not different_vendor_message:
raise UserError("Bisa langsung Confirm")
@@ -777,7 +831,17 @@ class PurchaseOrder(models.Model):
subtype_id=self.env.ref("mail.mt_note").id
)
-
+ def po_approve_unlock(self):
+ if self.approval_status_unlock == 'pengajuanFinance':
+ if self.env.user.is_accounting:
+ self.approval_status_unlock = 'approvedFinance'
+ else:
+ raise UserError("Menunggu persetujuan Finance jika ingin unlock PO")
+ elif self.approval_status_unlock == 'approvedFinance':
+ raise UserError("PO bisa langsung di unlock")
+ else:
+ raise UserError("Menunggu persetujuan Finance jika ingin unlock PO")
+
def check_different_vendor_so(self):
vendor_po = self.partner_id.id
message = ''
@@ -851,6 +915,13 @@ class PurchaseOrder(models.Model):
sales_price -= sale_order_line.fee_third_party_line
sum_sales_price += sales_price
purchase_price = line.price_subtotal
+ if line.ending_price > 0:
+ if line.taxes_id.id == 22:
+ ending_price = line.ending_price / 1.11
+ purchase_price = ending_price
+ else:
+ purchase_price = line.ending_price
+ # 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:
@@ -894,13 +965,18 @@ class PurchaseOrder(models.Model):
sales_price -= (sale_order_line.fee_third_party_line / sale_order_line.product_uom_qty) * line.qty_po
sum_sales_price += sales_price
- purchase_price = po_line.price_subtotal / po_line.product_qty * line.qty_po
+ if po_line.ending_price > 0:
+ price = po_line.ending_price
+ else:
+ price = po_line.price_subtotal
+ purchase_price = price / 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
+
if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0:
self.total_so_margin = sum_so_margin
@@ -921,3 +997,51 @@ class PurchaseOrder(models.Model):
if line.product_id.type == 'product':
sum_price_total += line.price_total
order.amount_total_without_service = sum_price_total
+
+ def button_unlock(self):
+ for order in self:
+ # Check if any order line has received_qty not equal to 0
+ if self.env.user.is_accounting:
+ order.state = 'purchase'
+ order.approval_status_unlock = 'approved'
+ break
+ for line in order.order_line:
+ if line.qty_received > 0:
+ if order.approval_status_unlock == 'approvedFinance':
+ order.approval_status_unlock = 'approved'
+ order.state = 'purchase'
+ break
+ if order.approval_status_unlock == 'pengajuanFinance':
+ raise UserError(_(
+ "Menunggu Approve Dari Finance."
+ ))
+ else:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': _('Untuk mengubah PO butuh approve dari Finance. Berikan alasan anda unlock PO!'),
+ 'res_model': 'purchase.order.unlock.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_purchase_order_id': order.id
+ }
+ }
+
+ return super(PurchaseOrder, self).button_unlock()
+
+
+class PurchaseOrderUnlockWizard(models.TransientModel):
+ _name = 'purchase.order.unlock.wizard'
+ _description = 'Wizard untuk memberikan alasan unlock PO'
+
+ purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order', required=True)
+ alasan = fields.Text(string='Alasan', required=True)
+
+ def confirm_reject(self):
+ order = self.purchase_order_id
+ if order:
+ order.write({'reason_unlock': self.alasan})
+ order.approval_status_unlock = 'pengajuanFinance'
+ return {'type': 'ir.actions.act_window_close'}
+
+
diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py
index 9e7d7e81..587a09a1 100755
--- a/indoteknik_custom/models/purchase_order_line.py
+++ b/indoteknik_custom/models/purchase_order_line.py
@@ -43,7 +43,79 @@ class PurchaseOrderLine(models.Model):
qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved')
delete_line = fields.Boolean(string='Delete', default=False, help='centang ini jika anda ingin menghapus line ini')
is_edit_product_qty = fields.Boolean(string='Is Edit Product Qty', compute='_compute_is_edit_product_qty')
-
+ delivery_amt = fields.Float(string='Delivery Amt', compute='_compute_doc_delivery_amt')
+ delivery_amt_per_item = fields.Float(string='Delivery Amt Per Item' , compute='_compute_doc_delivery_amt')
+ contribution_delivery_amt = fields.Float(string='Contribution Delivery Amt', compute='_compute_doc_delivery_amt')
+ cost_service = fields.Float(string='Biaya Jasa', compute='_compute_doc_delivery_amt')
+ cost_service_per_item = fields.Float(string='Biaya Jasa Per Item', compute='_compute_doc_delivery_amt')
+ contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt')
+ ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt')
+
+ def _compute_doc_delivery_amt(self):
+ for line in self:
+ # Inisialisasi nilai default untuk field computed
+ line.delivery_amt = 0.0
+ line.delivery_amt_per_item = 0.0
+ line.contribution_delivery_amt = 0.0
+ line.cost_service = 0.0
+ line.cost_service_per_item = 0.0
+ line.contribution_cost_service = 0.0
+ line.ending_price = line.price_unit * line.product_qty
+
+ # Ambil nilai dari order_id
+ total_delivery_amt = line.order_id.total_delivery_amt
+ total_cost_service = line.order_id.total_cost_service
+
+ include_price = line.price_unit * line.product_qty
+ if line.order_id.amount_total > 0:
+ if total_delivery_amt > 0:
+ contributions = include_price / line.order_id.amount_total
+ if line.taxes_id.id == 22:
+ contributions = line.price_subtotal / line.order_id.amount_untaxed
+ contribution = contributions * total_delivery_amt
+ line.delivery_amt = contribution
+ line.delivery_amt_per_item = contribution / line.product_qty
+ line.contribution_delivery_amt = contributions
+
+ if total_cost_service > 0:
+ contributions = include_price / line.order_id.amount_total
+ if line.taxes_id.id == 22:
+ contributions = line.price_subtotal / line.order_id.amount_untaxed
+ contribution = contributions * total_cost_service
+ line.cost_service = contribution
+ line.cost_service_per_item = contribution / line.product_qty
+ line.contribution_cost_service = contributions
+
+ if total_delivery_amt > 0 and total_cost_service > 0:
+ line.ending_price = (line.price_unit + line.delivery_amt_per_item + line.cost_service_per_item) * line.product_qty
+ elif total_delivery_amt > 0 and total_cost_service == 0:
+ line.ending_price = (line.price_unit + line.delivery_amt_per_item) * line.product_qty
+ elif total_delivery_amt == 0 and total_cost_service > 0:
+ line.ending_price = (line.price_unit + line.cost_service_per_item) * line.product_qty
+
+ # @api.constrains('delivery_amt')
+ # def delivery_amt_margin(self):
+ # for line in self:
+ # if line.delivery_amt:
+ # line.delivery_amt_per_item = line.delivery_amt / line.product_qty
+ # line.ending_price = line.price_unit + line.delivery_amt_per_item + line.cost_service
+ # elif line.delivery_amt == 0:
+ # line. delivery_amt_per_item = 0
+ # if line.cost_service:
+ # line.ending_price = line.price_unit + line.cost_service
+ # else:
+ # line.ending_price = line.price_unit
+
+ # @api.constrains('cost_service')
+ # def cost_service_margin(self):
+ # for line in self:
+ # if line.cost_service:
+ # line.ending_price = line.price_unit + line.cost_service + line.delivery_amt_per_item
+ # elif line.cost_service == 0:
+ # if line.delivery_amt_per_item:
+ # line.ending_price = line.price_unit + line.delivery_amt_per_item
+ # else:
+ # line.ending_price = line.price_unit
def _get_clean_website_description_product(self):
for line in self:
description = line.product_id.website_description
diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py
index 32a9f94f..c972b485 100644
--- a/indoteknik_custom/models/requisition.py
+++ b/indoteknik_custom/models/requisition.py
@@ -82,11 +82,11 @@ class Requisition(models.Model):
state = ['done', 'sale']
if self.sale_order_id.state in state:
raise UserError('SO sudah Confirm, akan berakibat double Purchase melalui PJ')
- if self.env.user.id not in [377, 19]:
+ if self.env.user.id not in [377, 19, 28]:
raise UserError('Hanya Vita dan Darren Yang Bisa Approve')
- if self.env.user.id == 377:
+ if self.env.user.id == 377 or self.env.user.id == 28:
self.sales_approve = True
- elif self.env.user.id == 19:
+ elif self.env.user.id == 19 or self.env.user.id == 28:
if not self.sales_approve:
raise UserError('Vita Belum Approve')
self.merchandise_approve = True
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index 4297012c..f081e274 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -1,7 +1,7 @@
from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
-
+from odoo.http import request
class GroupPartner(models.Model):
_name = 'group.partner'
@@ -20,6 +20,78 @@ class ResPartner(models.Model):
domain="[('internal_type', '=', 'receivable'), ('deprecated', '=', False), ('company_id', '=', current_company_id)]",
help="This account will be used instead of the default one as the receivable account for the current partner",
default=395)
+ # Referensi
+ supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers")
+
+ # informasi perusahaan
+ name_tempo = fields.Many2one('res.partner', string='Nama Perusahaan',tracking=True)
+ industry_id_tempo = fields.Many2one('res.partner.industry', 'Customer Industry', readonly=True)
+ street_tempo = fields.Char(string="Alamat Perusahaan")
+ state_id_tempo = fields.Many2one('res.country.state', string='State')
+ city_id_tempo = fields.Many2one('vit.kota', string='City')
+ zip_tempo = fields.Char(string="Zip")
+ mobile_tempo = fields.Char(string="No. Telfon Perusahaan")
+ bank_name_tempo = fields.Char(string="Nama Bank")
+ account_name_tempo = fields.Char(string="Nama Rekening")
+ account_number_tempo = fields.Char(string="Nomor Rekening Bank")
+ website_tempo = fields.Char(string='Website')
+ portal = fields.Boolean(string='Portal Website')
+ estimasi_tempo = fields.Char(string='Estimasi Pembelian Pertahun')
+ tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo')
+ tempo_limit = fields.Char(string='Limit Tempo')
+ category_produk_ids = fields.Many2many('product.public.category', string='Kategori Produk yang Digunakan', domain=lambda self: self._get_default_category_domain())
+
+ @api.model
+ def _get_default_category_domain(self):
+ return [('parent_id', '=', False)]
+
+ # Kontak Perusahaan
+ direktur_name = fields.Char(string='Nama Lengkap Direktur')
+ direktur_mobile = fields.Char(string='No. Telpon Direktur')
+ direktur_email = fields.Char(string='Email Direktur')
+ purchasing_name = fields.Char(string='Nama Purchasing')
+ purchasing_mobile = fields.Char(string='No. Telpon Purchasing')
+ purchasing_email = fields.Char(string='Email Purchasing')
+ finance_name = fields.Char(string='Nama Finance')
+ finance_mobile = fields.Char(string='No. Telpon Finance')
+ finance_email = fields.Char(string='Email Finance')
+
+ # Pengiriman
+ pic_name = fields.Char(string='Nama PIC Penerimaan Barang')
+ street_pengiriman = fields.Char(string="Alamat Perusahaan")
+ state_id_pengiriman = fields.Many2one('res.country.state', string='State')
+ city_id_pengiriman = fields.Many2one('vit.kota', string='City')
+ district_id_pengiriman = fields.Many2one('vit.kecamatan', string='Kecamatan')
+ subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan')
+ zip_pengiriman = fields.Char(string="Zip")
+ invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice')
+ street_invoice = fields.Char(string="Alamat Perusahaan")
+ state_id_invoice = fields.Many2one('res.country.state', string='State')
+ city_id_invoice = fields.Many2one('vit.kota', string='City')
+ district_id_invoice = fields.Many2one('vit.kecamatan', string='Kecamatan')
+ subDistrict_id_invoice = fields.Many2one('vit.kelurahan', string='Kelurahan')
+ zip_invoice = fields.Char(string="Zip")
+ tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice')
+ jadwal_bayar = fields.Char(string='Jadwal Pembayaran')
+ dokumen_pengiriman = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang')
+ dokumen_pengiriman_input = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang')
+ dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice')
+
+ # Dokumen
+ dokumen_npwp = fields.Many2one('ir.attachment', string="NPWP Perusahaan", tracking=3, readonly=True)
+ dokumen_sppkp = fields.Many2one('ir.attachment', string="SPPKP Perusahaan", tracking=3, readonly=True)
+ dokumen_nib = fields.Many2one('ir.attachment', string="NIB (SIUP/TDP/SKDP)", tracking=3, readonly=True,)
+ dokumen_siup = fields.Many2one('ir.attachment', string="SIUP Perusahaan", tracking=3, readonly=True)
+ dokumen_tdp = fields.Many2one('ir.attachment', string="TDP Perusahaan", tracking=3, readonly=True)
+ dokumen_skdp = fields.Many2one('ir.attachment', string="SKDP Perusahaan",tracking=True, readonly=True)
+ dokumen_skt = fields.Many2one('ir.attachment', string="SKT Perusahaan", tracking=True, readonly=True)
+ dokumen_akta_perubahan = fields.Many2one('ir.attachment', string="Akta Perubahan", tracking=3, readonly=True)
+ dokumen_ktp_dirut = fields.Many2one('ir.attachment', string="KTP Dirut/Direktur", tracking=3, readonly=True)
+ dokumen_akta_pendirian = fields.Many2one('ir.attachment', string="Akta Pendirian", tracking=3, readonly=True)
+ dokumen_laporan_keuangan = fields.Many2one('ir.attachment', string="Laporan Keuangan", tracking=3, readonly=True)
+ dokumen_foto_kantor = fields.Many2one('ir.attachment', string=" Foto Kantor (Tampak Depan)", tracking=3, readonly=True)
+ dokumen_tempat_bekerja = fields.Many2one('ir.attachment', string="Tempat Bekerja", tracking=3, readonly=True)
+
reference_number = fields.Char(string="Reference Number")
company_type_id = fields.Many2one('res.partner.company_type', string='Company Type')
custom_pricelist_id = fields.Many2one('product.pricelist', string='Price Matrix')
@@ -30,6 +102,7 @@ class ResPartner(models.Model):
])
sppkp = fields.Char(string="SPPKP", tracking=True)
npwp = fields.Char(string="NPWP", tracking=True)
+ nitku = fields.Char(string="NITKU", tracking=True)
counter = fields.Integer(string="Counter", default=0)
leadtime = fields.Integer(string="Leadtime", default=0)
digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak")
@@ -92,23 +165,22 @@ class ResPartner(models.Model):
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
-
+ # partner.alamat_lengkap_text = partner.parent_id.alamat_lengkap_text
- alamat_lengkap_text = fields.Text(string="Alamat Lengkap", required=False , tracking=3)
+ 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')
- #
+ #
+ # # 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
def write(self, vals):
@@ -133,8 +205,74 @@ class ResPartner(models.Model):
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)
+ vals['company_type_id'] = vals.get('company_type_id', self.company_type_id.id if self.company_type_id else None)
+
+ # Referensi
+ vals['supplier_ids'] = vals.get('supplier_ids', self.supplier_ids)
+
+ # informasi perusahaan
+ vals['name_tempo'] = vals.get('name_tempo', self.name_tempo)
+ vals['industry_id_tempo'] = vals.get('industry_id_tempo', self.industry_id_tempo)
+ vals['street_tempo'] = vals.get('street_tempo', self.street_tempo)
+ vals['state_id_tempo'] = vals.get('state_id_tempo', self.state_id_tempo)
+ vals['city_id_tempo'] = vals.get('city_id_tempo', self.city_id_tempo)
+ vals['zip_tempo'] = vals.get('zip_tempo', self.zip_tempo)
+ vals['bank_name_tempo'] = vals.get('bank_name_tempo', self.bank_name_tempo)
+ vals['account_name_tempo'] = vals.get('account_name_tempo', self.account_name_tempo)
+ vals['account_number_tempo'] = vals.get('account_number_tempo', self.account_number_tempo)
+ vals['website_tempo'] = vals.get('website_tempo', self.website_tempo)
+ vals['portal'] = vals.get('portal', self.portal)
+ vals['estimasi_tempo'] = vals.get('estimasi_tempo', self.estimasi_tempo)
+ vals['tempo_duration'] = vals.get('tempo_duration', self.tempo_duration)
+ vals['tempo_limit'] = vals.get('tempo_limit', self.tempo_limit)
+ vals['category_produk_ids'] = vals.get('category_produk_ids', self.category_produk_ids)
+
+ # Kontak Perusahaan
+ vals['direktur_name'] = vals.get('direktur_name', self.direktur_name)
+ vals['direktur_mobile'] = vals.get('direktur_mobile', self.direktur_mobile)
+ vals['direktur_email'] = vals.get('direktur_email', self.direktur_email)
+ vals['purchasing_name'] = vals.get('purchasing_name', self.purchasing_name)
+ vals['purchasing_mobile'] = vals.get('purchasing_mobile', self.purchasing_mobile)
+ vals['purchasing_email'] = vals.get('purchasing_email', self.purchasing_email)
+ vals['finance_name'] = vals.get('finance_name', self.finance_name)
+ vals['finance_mobile'] = vals.get('finance_mobile', self.finance_mobile)
+ vals['finance_email'] = vals.get('finance_email', self.finance_email)
+
+ # Pengiriman
+ vals['pic_name'] = vals.get('pic_name', self.pic_name)
+ vals['street_pengiriman'] = vals.get('street_pengiriman', self.street_pengiriman)
+ vals['state_id_pengiriman'] = vals.get('state_id_pengiriman', self.state_id_pengiriman)
+ vals['city_id_pengiriman'] = vals.get('city_id_pengiriman', self.city_id_pengiriman)
+ vals['district_id_pengiriman'] = vals.get('district_id_pengiriman', self.district_id_pengiriman)
+ vals['subDistrict_id_pengiriman'] = vals.get('subDistrict_id_pengiriman', self.subDistrict_id_pengiriman)
+ vals['zip_pengiriman'] = vals.get('zip_pengiriman', self.zip_pengiriman)
+ vals['invoice_pic'] = vals.get('invoice_pic', self.invoice_pic)
+ vals['street_invoice'] = vals.get('street_invoice', self.street_invoice)
+ vals['state_id_invoice'] = vals.get('state_id_invoice', self.state_id_invoice)
+ vals['city_id_invoice'] = vals.get('city_id_invoice', self.city_id_invoice)
+ vals['district_id_invoice'] = vals.get('district_id_invoice', self.district_id_invoice)
+ vals['subDistrict_id_invoice'] = vals.get('subDistrict_id_invoice', self.subDistrict_id_invoice)
+ vals['zip_invoice'] = vals.get('zip_invoice', self.zip_invoice)
+ vals['tukar_invoice'] = vals.get('tukar_invoice', self.tukar_invoice)
+ vals['jadwal_bayar'] = vals.get('jadwal_bayar', self.jadwal_bayar)
+ vals['dokumen_pengiriman'] = vals.get('dokumen_pengiriman', self.dokumen_pengiriman)
+ vals['dokumen_pengiriman_input'] = vals.get('dokumen_pengiriman_input', self.dokumen_pengiriman_input)
+ vals['dokumen_invoice'] = vals.get('dokumen_invoice', self.dokumen_invoice)
+
+ # Dokumen
+ vals['dokumen_npwp'] = vals.get('dokumen_npwp', self.dokumen_npwp)
+ vals['dokumen_sppkp'] = vals.get('dokumen_sppkp', self.dokumen_sppkp)
+ vals['dokumen_nib'] = vals.get('dokumen_nib', self.dokumen_nib)
+ vals['dokumen_siup'] = vals.get('dokumen_siup', self.dokumen_siup)
+ vals['dokumen_tdp'] = vals.get('dokumen_tdp', self.dokumen_tdp)
+ vals['dokumen_skdp'] = vals.get('dokumen_skdp', self.dokumen_skdp)
+ vals['dokumen_skt'] = vals.get('dokumen_skt', self.dokumen_skt)
+ vals['dokumen_akta_perubahan'] = vals.get('dokumen_akta_perubahan', self.dokumen_akta_perubahan)
+ vals['dokumen_ktp_dirut'] = vals.get('dokumen_ktp_dirut', self.dokumen_ktp_dirut)
+ vals['dokumen_akta_pendirian'] = vals.get('dokumen_akta_pendirian', self.dokumen_akta_pendirian)
+ vals['dokumen_laporan_keuangan'] = vals.get('dokumen_laporan_keuangan', self.dokumen_laporan_keuangan)
+ vals['dokumen_foto_kantor'] = vals.get('dokumen_foto_kantor', self.dokumen_foto_kantor)
+ vals['dokumen_tempat_bekerja'] = vals.get('dokumen_tempat_bekerja', self.dokumen_tempat_bekerja)
# Simpan hanya field yang perlu di-update pada child
vals_for_child = {
@@ -144,7 +282,67 @@ class ResPartner(models.Model):
'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')
+ 'company_type_id': vals.get('company_type_id'),
+ 'supplier_ids': vals.get('supplier_ids'),
+ 'name_tempo': vals.get('name_tempo'),
+ 'industry_id_tempo': vals.get('industry_id_tempo'),
+ 'street_tempo': vals.get('street_tempo'),
+ 'state_id_tempo': vals.get('state_id_tempo'),
+ 'city_id_tempo': vals.get('city_id_tempo'),
+ 'zip_tempo': vals.get('zip_tempo'),
+ 'bank_name_tempo': vals.get('bank_name_tempo'),
+ 'account_name_tempo': vals.get('account_name_tempo'),
+ 'account_number_tempo': vals.get('account_number_tempo'),
+ 'website_tempo': vals.get('website_tempo'),
+ 'portal': vals.get('portal'),
+ 'estimasi_tempo': vals.get('estimasi_tempo'),
+ 'tempo_duration': vals.get('tempo_duration'),
+ 'tempo_limit': vals.get('tempo_limit'),
+ 'category_produk_ids': vals.get('category_produk_ids'),
+ 'direktur_name': vals.get('direktur_name'),
+ 'direktur_mobile': vals.get('direktur_mobile'),
+ 'direktur_email': vals.get('direktur_email'),
+ 'purchasing_name': vals.get('purchasing_name'),
+ 'purchasing_mobile': vals.get('purchasing_mobile'),
+ 'purchasing_email': vals.get('purchasing_email'),
+ 'finance_name': vals.get('finance_name'),
+ 'finance_mobile': vals.get('finance_mobile'),
+ 'finance_email': vals.get('finance_email'),
+ 'pic_name': vals.get('pic_name'),
+ 'street_pengiriman': vals.get('street_pengiriman'),
+ 'state_id_pengiriman': vals.get('state_id_pengiriman'),
+ 'city_id_pengiriman': vals.get('city_id_pengiriman'),
+ 'district_id_pengiriman': vals.get('district_id_pengiriman'),
+ 'subDistrict_id_pengiriman': vals.get('subDistrict_id_pengiriman'),
+ 'zip_pengiriman': vals.get('zip_pengiriman'),
+ 'invoice_pic': vals.get('invoice_pic'),
+ 'street_invoice': vals.get('street_invoice'),
+ 'state_id_invoice': vals.get('state_id_invoice'),
+ 'city_id_invoice': vals.get('city_id_invoice'),
+ 'district_id_invoice': vals.get('district_id_invoice'),
+ 'subDistrict_id_invoice': vals.get('subDistrict_id_invoice'),
+ 'zip_invoice': vals.get('zip_invoice'),
+ 'tukar_invoice': vals.get('tukar_invoice'),
+ 'jadwal_bayar': vals.get('jadwal_bayar'),
+ 'dokumen_pengiriman': vals.get('dokumen_pengiriman'),
+ 'dokumen_pengiriman_input': vals.get('dokumen_pengiriman_input'),
+ 'dokumen_invoice': vals.get('dokumen_invoice'),
+ 'dokumen_npwp': vals.get('dokumen_npwp'),
+ 'dokumen_sppkp': vals.get('dokumen_sppkp'),
+ 'dokumen_nib': vals.get('dokumen_nib'),
+ 'dokumen_siup': vals.get('dokumen_siup'),
+ 'dokumen_tdp': vals.get('dokumen_tdp'),
+ 'dokumen_skdp': vals.get('dokumen_skdp'),
+ 'dokumen_skt': vals.get('dokumen_skt'),
+ 'dokumen_akta_perubahan': vals.get('dokumen_akta_perubahan'),
+ 'dokumen_ktp_dirut': vals.get('dokumen_ktp_dirut'),
+ 'dokumen_akta_pendirian': vals.get('dokumen_akta_pendirian'),
+ 'dokumen_laporan_keuangan': vals.get('dokumen_laporan_keuangan'),
+ 'dokumen_foto_kantor': vals.get('dokumen_foto_kantor'),
+ 'dokumen_tempat_bekerja': vals.get('dokumen_tempat_bekerja'),
+
+ # internal_notes
+ 'comment': vals.get('comment')
}
# Lakukan update pada semua child secara rekursif
@@ -244,6 +442,18 @@ class ResPartner(models.Model):
def _onchange_customer_type(self):
if self.customer_type == 'nonpkp':
self.npwp = '00.000.000.0-000.000'
-
+ def get_check_payment_term(self):
+ self.ensure_one()
+
+ partner = self.parent_id or self
+
+ return partner.property_payment_term_id.name if partner.property_payment_term_id.id else 'Cash Before Delivery (C.B.D)'
+ @api.constrains('nitku')
+ def _onchange_nitku(self):
+ if self.nitku:
+ if not self.nitku.isdigit():
+ raise UserError("NITKU harus berupa angka.")
+ if len(self.nitku) != 22:
+ raise UserError("NITKU harus memiliki tepat 22 angka.") \ No newline at end of file
diff --git a/indoteknik_custom/models/res_users.py b/indoteknik_custom/models/res_users.py
index fce48b30..31b84ae3 100755
--- a/indoteknik_custom/models/res_users.py
+++ b/indoteknik_custom/models/res_users.py
@@ -56,6 +56,11 @@ class ResUsers(models.Model):
for user in self:
template.send_mail(user.id, force_send=True)
+ def send_company_request_tempo_review(self):
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_review')
+ 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 315a338a..7b2d9bf8 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -99,6 +99,7 @@ class SaleOrder(models.Model):
helper_by_id = fields.Many2one('res.users', 'Helper By')
eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date')
flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web')
+ is_continue_transaction = fields.Boolean(string='Button Transaction', help='Data dari web')
web_approval = fields.Selection([
('company', 'Company'),
('cust_manager', 'Customer Manager'),
@@ -144,6 +145,14 @@ class SaleOrder(models.Model):
('NP', 'Non Pareto')
])
+ @api.onchange('payment_status')
+ def _is_continue_transaction(self):
+ if not self.is_continue_transaction:
+ if self.payment_status == 'settlement':
+ self.is_continue_transaction = True
+ else:
+ self.is_continue_transaction = False
+
def _compute_total_weight(self):
total_weight = 0
missing_weight_products = []
@@ -749,6 +758,7 @@ class SaleOrder(models.Model):
raise UserError("Salesperson sudah tidak aktif, mohon diisi yang benar pada data SO dan Contact")
def sale_order_approve(self):
+ self.check_credit_limit()
if self.validate_different_vendor() and not self.vendor_approval:
return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
self.check_due()
@@ -881,6 +891,37 @@ class SaleOrder(models.Model):
'email_to': salesperson_email,
}).send()
+ def check_credit_limit(self):
+ for rec in self:
+ outstanding_amount = rec.outstanding_amount
+ check_credit_limit = False
+ ######
+ block_stage = 0
+ if rec.partner_id.parent_id:
+ if rec.partner_id.parent_id.active_limit and rec.partner_id.parent_id.enable_credit_limit:
+ check_credit_limit = True
+ else:
+ if rec.partner_id.active_limit and rec.partner_id.enable_credit_limit:
+ check_credit_limit = True
+
+ term_days = 0
+ for term_line in rec.payment_term_id.line_ids:
+ term_days += term_line.days
+ if term_days == 0:
+ check_credit_limit = False
+
+ if check_credit_limit:
+ if rec.partner_id.parent_id:
+ block_stage = rec.partner_id.parent_id.blocking_stage or 0
+ else:
+ block_stage = rec.partner_id.blocking_stage or 0
+
+ if (outstanding_amount + rec.amount_total) >= block_stage:
+ if block_stage != 0:
+ remaining_credit_limit = block_stage - outstanding_amount
+ raise UserError(_("%s is in Blocking Stage, Remaining credit limit is %s, from %s and outstanding %s")
+ % (rec.partner_id.name, remaining_credit_limit, block_stage, outstanding_amount))
+
def validate_different_vendor(self):
if self.vendor_approval_id.filtered(lambda v: v.state == 'draft'):
draft_names = ", ".join(self.vendor_approval_id.filtered(lambda v: v.state == 'draft').mapped('number'))
@@ -990,8 +1031,9 @@ class SaleOrder(models.Model):
if self.have_outstanding_invoice:
raise UserError("Invoice harus di Cancel dahulu")
- elif self.have_outstanding_picking:
- raise UserError("DO harus di Cancel dahulu")
+ for line in self.order_line:
+ if line.qty_delivered > 0:
+ raise UserError("DO harus di-cancel terlebih dahulu.")
if not self.web_approval:
self.web_approval = 'company'
@@ -1369,4 +1411,16 @@ class SaleOrder(models.Model):
'npwp': partner.npwp,
'email': partner.email,
'customer_type': partner.customer_type,
- }) \ No newline at end of file
+ })
+
+ def write(self, vals):
+ for order in self:
+ if order.state in ['sale', 'cancel']:
+ if 'order_line' in vals:
+ new_lines = vals.get('order_line', [])
+ for command in new_lines:
+ if command[0] == 0: # A new line is being added
+ raise UserError(
+ "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.")
+ res = super(SaleOrder, self).write(vals)
+ return res \ 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 04fafa69..29a046fa 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -33,11 +33,19 @@ class SaleOrderLine(models.Model):
qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved')
product_available_quantity = fields.Float(string='Qty pickup by user',)
reserved_from = fields.Char(string='Reserved From', copy=False)
- item_percent_margin_without_deduction = fields.Float('%Margin', compute='_compute_item_margin_without_deduction')
+ item_percent_margin_without_deduction = fields.Float('Margin Without Deduction', compute='_compute_item_margin_without_deduction')
weight = fields.Float(string='Weight')
md_vendor_id = fields.Many2one('res.partner', string='MD Vendor', readonly=True)
margin_md = fields.Float(string='Margin MD')
qty_free_bu = fields.Float(string='Free BU', compute='_get_qty_free_bandengan')
+ desc_updatable = fields.Boolean(string='desc boolean', default=False, compute='_get_desc_updatable')
+
+ def _get_desc_updatable(self):
+ for line in self:
+ if line.product_id.id != 417724:
+ line.desc_updatable = False
+ else:
+ line.desc_updatable = True
def _get_qty_free_bandengan(self):
for line in self:
@@ -272,6 +280,10 @@ class SaleOrderLine(models.Model):
(line.product_id.short_spesification if line.product_id.short_spesification else '')
line.name = line_name
line.weight = line.product_id.weight
+ if line.product_id.id != 417724:
+ line.desc_updatable = False
+ else:
+ line.desc_updatable = True
@api.constrains('vendor_id')
def _check_vendor_id(self):
@@ -378,4 +390,14 @@ class SaleOrderLine(models.Model):
if not line.product_id.product_tmpl_id.sale_ok:
raise UserError('Product %s belum bisa dijual, harap hubungi finance' % line.product_id.display_name)
if not line.vendor_id or not line.purchase_price and not line.display_type == 'line_note':
- raise UserError(_('Isi Vendor dan Harga Beli sebelum Request Approval')) \ No newline at end of file
+ raise UserError(_('Isi Vendor dan Harga Beli sebelum Request Approval'))
+
+ @api.depends('state')
+ def _compute_product_updatable(self):
+ for line in self:
+ if line.state == 'draft':
+ line.product_updatable = True
+ # line.desc_updatable = True
+ else:
+ line.product_updatable = False
+ # line.desc_updatable = False
diff --git a/indoteknik_custom/models/solr/apache_solr.py b/indoteknik_custom/models/solr/apache_solr.py
index 6560c9b5..d111c1c1 100644
--- a/indoteknik_custom/models/solr/apache_solr.py
+++ b/indoteknik_custom/models/solr/apache_solr.py
@@ -22,7 +22,7 @@ class ApacheSolr(models.Model):
url = ''
if env == 'development':
- url = 'http://192.168.23.5:8983/solr/'
+ url = 'http://localhost:8983/solr/'
elif env == 'production':
url = 'http://34.101.189.218:8983/solr/'
diff --git a/indoteknik_custom/models/stock_immediate_transfer.py b/indoteknik_custom/models/stock_immediate_transfer.py
new file mode 100644
index 00000000..4be0dff2
--- /dev/null
+++ b/indoteknik_custom/models/stock_immediate_transfer.py
@@ -0,0 +1,36 @@
+from odoo import models, api, _
+from odoo.exceptions import UserError
+
+class StockImmediateTransfer(models.TransientModel):
+ _inherit = 'stock.immediate.transfer'
+
+ def process(self):
+ """Override process method to add send_mail_bills logic."""
+ pickings_to_do = self.env['stock.picking']
+ pickings_not_to_do = self.env['stock.picking']
+
+ for line in self.immediate_transfer_line_ids:
+ if line.to_immediate is True:
+ pickings_to_do |= line.picking_id
+ else:
+ pickings_not_to_do |= line.picking_id
+
+ for picking in pickings_to_do:
+ picking.send_mail_bills()
+ # If still in draft => confirm and assign
+ if picking.state == 'draft':
+ picking.action_confirm()
+ if picking.state != 'assigned':
+ picking.action_assign()
+ if picking.state != 'assigned':
+ raise UserError(_("Could not reserve all requested products. Please use the 'Mark as Todo' button to handle the reservation manually."))
+ for move in picking.move_lines.filtered(lambda m: m.state not in ['done', 'cancel']):
+ for move_line in move.move_line_ids:
+ move_line.qty_done = move_line.product_uom_qty
+
+ pickings_to_validate = self.env.context.get('button_validate_picking_ids')
+ if pickings_to_validate:
+ pickings_to_validate = self.env['stock.picking'].browse(pickings_to_validate)
+ pickings_to_validate = pickings_to_validate - pickings_not_to_do
+ return pickings_to_validate.with_context(skip_immediate=True).button_validate()
+ return True
diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py
index ac2e3cc0..e1d4e74c 100644
--- a/indoteknik_custom/models/stock_move.py
+++ b/indoteknik_custom/models/stock_move.py
@@ -7,6 +7,27 @@ class StockMove(models.Model):
line_no = fields.Integer('No', default=0)
sale_id = fields.Many2one('sale.order', string='SO')
+ print_barcode = fields.Boolean(
+ string="Print Barcode",
+ default=lambda self: self.product_id.print_barcode,
+ )
+ qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant')
+
+ def _compute_qr_code_variant(self):
+ for rec in self:
+ if rec.print_barcode and rec.print_barcode == True and rec.product_id and rec.product_id.qr_code_variant:
+ rec.qr_code_variant = rec.product_id.qr_code_variant
+ else:
+ rec.qr_code_variant = False
+
+
+ def write(self, vals):
+ res = super(StockMove, self).write(vals)
+ if 'print_barcode' in vals:
+ for line in self:
+ if line.product_id:
+ line.product_id.print_barcode = vals['print_barcode']
+ return res
def _do_unreserve(self, product=None, quantity=False):
moves_to_unreserve = OrderedSet()
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 17dd5766..cd330aeb 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -1,13 +1,22 @@
from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools.float_utils import float_is_zero
-from datetime import datetime
+from datetime import timedelta, datetime
from itertools import groupby
-import pytz, datetime, requests, json
-
+import pytz, requests, json, requests
+from dateutil import parser
+import datetime
+import hmac
+import hashlib
+import base64
+import requests
+import time
+import logging
+_logger = logging.getLogger(__name__)
class StockPicking(models.Model):
_inherit = 'stock.picking'
+ # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True)
is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not')
account_id = fields.Many2one('account.account', string='Account')
efaktur_id = fields.Many2one('vit.efaktur', string='Faktur Pajak')
@@ -113,6 +122,213 @@ class StockPicking(models.Model):
], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.")
notee = fields.Text(string="Note")
+ @api.model
+ def _compute_dokumen_tanda_terima(self):
+ for picking in self:
+ picking.dokumen_tanda_terima = picking.partner_id.dokumen_pengiriman
+
+ @api.model
+ def _compute_dokumen_pengiriman(self):
+ for picking in self:
+ picking.dokumen_pengiriman = picking.partner_id.dokumen_pengiriman_input
+
+ dokumen_tanda_terima = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_tanda_terima)
+ dokumen_pengiriman = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_pengiriman)
+
+ # Envio Tracking Section
+ envio_id = fields.Char(string="Envio ID", readonly=True)
+ envio_code = fields.Char(string="Envio Code", readonly=True)
+ envio_ref_code = fields.Char(string="Envio Reference Code", readonly=True)
+ envio_eta_at = fields.Datetime(string="Estimated Time of Arrival (ETA)", readonly=True)
+ envio_ata_at = fields.Datetime(string="Actual Time of Arrival (ATA)", readonly=True)
+ envio_etd_at = fields.Datetime(string="Estimated Time of Departure (ETD)", readonly=True)
+ envio_atd_at = fields.Datetime(string="Actual Time of Departure (ATD)", readonly=True)
+ envio_received_by = fields.Char(string="Received By", readonly=True)
+ envio_status = fields.Char(string="Status", readonly=True)
+ envio_cod_value = fields.Float(string="COD Value", readonly=True)
+ envio_cod_status = fields.Char(string="COD Status", readonly=True)
+ envio_logs = fields.Text(string="Logs", readonly=True)
+ envio_latest_message = fields.Text(string="Latest Log Message", readonly=True)
+ envio_latest_recorded_at = fields.Datetime(string="Log Recorded At", readonly=True)
+ envio_latest_latitude = fields.Float(string="Log Latitude", readonly=True)
+ envio_latest_longitude = fields.Float(string="Log Longitude", readonly=True)
+ tracking_by = fields.Many2one('res.users', string='Tracking By', readonly=True, tracking=True)
+
+ # Lalamove Section
+ lalamove_order_id = fields.Char(string="Lalamove Order ID", copy=False)
+ lalamove_address = fields.Char(string="Lalamove Address")
+ lalamove_name = fields.Char(string="Lalamove Name")
+ lalamove_phone = fields.Char(string="Lalamove Phone")
+ lalamove_status = fields.Char(string="Lalamove Status")
+ lalamove_delivered_at = fields.Datetime(string="Lalamove Delivered At")
+ lalamove_data = fields.Text(string="Lalamove Data", readonly=True)
+ lalamove_image_url = fields.Char(string="Lalamove Image URL")
+ lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html")
+
+ def _compute_lalamove_image_html(self):
+ for record in self:
+ if record.lalamove_image_url:
+ record.lalamove_image_html = f'<img src="{record.lalamove_image_url}" width="300" height="300"/>'
+ else:
+ record.lalamove_image_html = "No image available."
+
+ def action_fetch_lalamove_order(self):
+ pickings = self.env['stock.picking'].search([
+ ('picking_type_code', '=', 'outgoing'),
+ ('state', '=', 'done'),
+ ('carrier_id', '=', 9)
+ ])
+ for picking in pickings:
+ try:
+ order_id = picking.lalamove_order_id
+ apikey = self.env['ir.config_parameter'].sudo().get_param('lalamove.apikey')
+ secret = self.env['ir.config_parameter'].sudo().get_param('lalamove.secret')
+ market = self.env['ir.config_parameter'].sudo().get_param('lalamove.market', default='ID')
+
+ order_data = picking.get_lalamove_order(order_id, apikey, secret, market)
+ picking.lalamove_data = order_data
+ except Exception as e:
+ _logger.error(f"Error fetching Lalamove order for picking {picking.id}: {str(e)}")
+ continue
+
+ def get_lalamove_order(self, order_id, apikey, secret, market):
+ timestamp = str(int(time.time() * 1000))
+ message = f"{timestamp}\r\nGET\r\n/v3/orders/{order_id}\r\n\r\n"
+ signature = hmac.new(secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
+
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"hmac {apikey}:{timestamp}:{signature}",
+ "Market": market
+ }
+
+ url = f"https://rest.lalamove.com/v3/orders/{order_id}"
+ response = requests.get(url, headers=headers)
+
+ if response.status_code == 200:
+ data = response.json()
+ stops = data.get("data", {}).get("stops", [])
+
+ for stop in stops:
+ pod = stop.get("POD", {})
+ if pod.get("status") == "DELIVERED":
+ image_url = pod.get("image") # Sesuaikan jika key berbeda
+ self.lalamove_image_url = image_url
+
+ address = stop.get("address")
+ name = stop.get("name")
+ phone = stop.get("phone")
+ delivered_at = pod.get("deliveredAt")
+
+ delivered_at_dt = self._convert_to_datetime(delivered_at)
+
+ self.lalamove_address = address
+ self.lalamove_name = name
+ self.lalamove_phone = phone
+ self.lalamove_status = pod.get("status")
+ self.lalamove_delivered_at = delivered_at_dt
+ return data
+
+ raise UserError("No delivered data found in Lalamove response.")
+ else:
+ raise UserError(f"Error {response.status_code}: {response.text}")
+
+
+ def _convert_to_wib(self, date_str):
+ """
+ Mengonversi string waktu ISO 8601 ke format waktu Indonesia (WIB)
+ """
+ if not date_str:
+ return False
+ try:
+ utc_time = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ')
+ wib_time = utc_time + timedelta(hours=7)
+ return wib_time.strftime('%d-%m-%Y %H:%M:%S')
+ except ValueError:
+ raise UserError(f"Format waktu tidak sesuai: {date_str}")
+
+ def _convert_to_datetime(self, date_str):
+ """Mengonversi string waktu dari API ke datetime."""
+ if not date_str:
+ return False
+ try:
+ # Format waktu dengan milidetik
+ date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%fZ')
+ return date
+ except ValueError:
+ try:
+ # Format waktu tanpa milidetik
+ date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ')
+ return date
+ except ValueError:
+ raise UserError(f"Format waktu tidak sesuai: {date_str}")
+
+ def track_envio_shipment(self):
+ pickings = self.env['stock.picking'].search([
+ ('picking_type_code', '=', 'outgoing'),
+ ('state', '=', 'done'),
+ ('carrier_id', '=', 151)
+ ])
+ for picking in pickings:
+ if not picking.name:
+ raise UserError("Name pada stock.picking tidak ditemukan.")
+
+ # API URL dan headers
+ url = f"https://api.envio.co.id/v1/tracking/distribution?code={picking.name}"
+ headers = {
+ 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo',
+ 'Content-Type': 'application/json',
+ }
+
+ try:
+ # Request ke API
+ response = requests.get(url, headers=headers, timeout=10)
+ response.raise_for_status() # Raise error jika status code bukan 200
+ response_data = response.json()
+
+ # Validasi jika respons tidak sesuai format yang diharapkan
+ if not response_data or "data" not in response_data:
+ raise UserError("Respons API tidak sesuai format yang diharapkan.")
+
+ data = response_data.get("data")
+ if not data:
+ continue
+
+ # Menyimpan data ke field masing-masing
+ picking.envio_id = data.get("id")
+ picking.envio_code = data.get("code")
+ picking.envio_ref_code = data.get("ref_code")
+ picking.envio_eta_at = self._convert_to_datetime(data.get("eta_at"))
+ picking.envio_ata_at = self._convert_to_datetime(data.get("ata_at"))
+ picking.envio_etd_at = self._convert_to_datetime(data.get("etd_at"))
+ picking.envio_atd_at = self._convert_to_datetime(data.get("atd_at"))
+ picking.envio_received_by = data.get("received_by")
+ picking.envio_status = data.get("status")
+ picking.envio_cod_value = data.get("cod_value", 0.0)
+ picking.envio_cod_status = data.get("cod_status")
+
+ # Menyimpan log terbaru
+ logs = data.get("logs", [])
+ if logs and isinstance(logs, list) and logs[0]:
+ latest_log = logs[0]
+ picking.envio_latest_message = latest_log.get("message", "Log kosong.")
+ picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at"))
+ picking.envio_latest_latitude = latest_log.get("latitude", 0.0)
+ picking.envio_latest_longitude = latest_log.get("longitude", 0.0)
+
+ picking.tracking_by = self.env.user.id
+ ata_at_str = data.get("ata_at")
+ envio_ata = self._convert_to_datetime(data.get("ata_at"))
+
+ picking.driver_arrival_date = envio_ata
+ if data.get("status") != 'delivered':
+ picking.driver_arrival_date = False
+ picking.envio_ata_at = False
+ except requests.exceptions.RequestException as e:
+ raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}")
+ except Exception as e:
+ raise UserError(f"Kesalahan tidak terduga: {str(e)}")
+
def action_send_to_biteship(self):
url = "https://api.biteship.com/v1/orders"
api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA"
@@ -545,13 +761,13 @@ class StockPicking(models.Model):
if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking':
if self.origin and 'Return of' in self.origin:
raise UserError("Button ini hanya untuk Logistik")
-
+
if self.picking_type_code == 'internal':
self.check_qty_done_stock()
if self._name != 'stock.picking':
return super(StockPicking, self).button_validate()
-
+
if not self.picking_code:
self.picking_code = self.env['ir.sequence'].next_by_code('stock.picking.code') or '0'
@@ -565,15 +781,10 @@ class StockPicking(models.Model):
raise UserError("Harus di Approve oleh Accounting")
if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver:
- raise UserError("Harus di Approve oleh Logistik")
+ 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:
- # if self.group_id.sale_id.payment_status != 'settlement' and self.group_id.sale_id.state == 'draft':
- # raise UserError('Uang belum masuk (settlement), mohon konfirmasi ke sales atau finance')
+ raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara")
if self.is_internal_use:
self.approval_status = 'approved'
@@ -591,7 +802,7 @@ class StockPicking(models.Model):
if not self.date_reserved:
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.date_reserved = current_time
-
+
self.validation_minus_onhand_quantity()
self.responsible = self.env.user.id
res = super(StockPicking, self).button_validate()
@@ -599,6 +810,59 @@ class StockPicking(models.Model):
self.date_done = datetime.datetime.utcnow()
self.state_reserve = 'done'
return res
+
+
+ def send_mail_bills(self):
+ if self.picking_type_code == 'incoming' and self.purchase_id:
+ template = self.env.ref('indoteknik_custom.mail_template_invoice_po_document')
+ if template and self.purchase_id:
+ # Render email body
+ email_values = template.sudo().generate_email(
+ res_ids=[self.purchase_id.id],
+ fields=['body_html']
+ )
+ rendered_body = email_values.get(self.purchase_id.id, {}).get('body_html', '')
+
+ # Render report dengan XML ID
+ report = self.env.ref('purchase.action_report_purchase_order') # Gunakan XML ID laporan
+ if not report:
+ raise UserError("Laporan dengan XML ID 'purchase.action_report_purchase_order' tidak ditemukan.")
+
+ # Render laporan ke PDF
+ pdf_content, _ = report._render_qweb_pdf([self.purchase_id.id])
+ report_content = base64.b64encode(pdf_content).decode('utf-8')
+
+ # Kirim email menggunakan template
+ email_sent = template.sudo().send_mail(self.purchase_id.id, force_send=True)
+
+ if email_sent:
+ # Buat attachment untuk laporan
+ attachment = self.env['ir.attachment'].create({
+ 'name': self.purchase_id.name or "Laporan Invoice.pdf",
+ 'type': 'binary',
+ 'datas': report_content,
+ 'res_model': 'purchase.order',
+ 'res_id': self.purchase_id.id,
+ 'mimetype': 'application/pdf',
+ })
+
+ # Siapkan data untuk mail.compose.message
+ compose_values = {
+ 'subject': "Pengiriman Email Invoice",
+ 'body': rendered_body,
+ 'attachment_ids': [(4, attachment.id)],
+ 'res_id': self.purchase_id.id,
+ 'model': 'purchase.order',
+ }
+
+ # Buat mail.compose.message
+ compose_message = self.env['mail.compose.message'].create(compose_values)
+
+ # Kirim pesan melalui wizard
+ compose_message.action_send_mail()
+
+ return True
+
def action_cancel(self):
if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking':
if self.origin and 'Return of' in self.origin:
@@ -742,4 +1006,50 @@ class StockPicking(models.Model):
formatted_fastest_eta = fastest_eta.strftime(format_time_fastest)
formatted_longest_eta = longest_eta.strftime(format_time)
- return f'{formatted_fastest_eta} - {formatted_longest_eta}' \ No newline at end of file
+ return f'{formatted_fastest_eta} - {formatted_longest_eta}'
+
+# class CheckProduct(models.Model):
+# _name = 'check.product'
+# _description = 'Check Product'
+# _order = 'picking_id, id'
+
+# picking_id = fields.Many2one('stock.picking', string='Picking Reference', required=True, ondelete='cascade', index=True, copy=False)
+# product_id = fields.Many2one('product.product', string='Product')
+
+
+# @api.constrains('product_id')
+# def check_product_validity(self):
+# """
+# Validate if the product exists in the related stock.picking's move_ids_without_package
+# and ensure that the product's quantity does not exceed the available product_uom_qty.
+# """
+# for record in self:
+# if not record.picking_id or not record.product_id:
+# continue
+
+# # Filter move lines in the related picking for the selected product
+# moves = record.picking_id.move_ids_without_package.filtered(
+# lambda move: move.product_id.id == record.product_id.id
+# )
+
+# if not moves:
+# raise UserError((
+# "The product '%s' is not available in the related stock picking's moves. "
+# "Please check and try again."
+# ) % record.product_id.display_name)
+
+# # Calculate the total entries for the product in check.product for the same picking
+# product_entries_count = self.search_count([
+# ('picking_id', '=', record.picking_id.id),
+# ('product_id', '=', record.product_id.id)
+# ])
+
+# # Sum the product_uom_qty for all relevant moves
+# total_qty_in_moves = sum(moves.mapped('product_uom_qty'))
+
+# # Compare the count of entries against the available quantity
+# if product_entries_count > total_qty_in_moves:
+# raise UserError((
+# "The product '%s' exceeds the allowable quantity (%s) in the related stock picking's moves. "
+# "You can only add it %s times."
+# ) % (record.product_id.display_name, total_qty_in_moves, total_qty_in_moves))
diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py
index 91a3a9fd..d4347235 100644
--- a/indoteknik_custom/models/stock_picking_return.py
+++ b/indoteknik_custom/models/stock_picking_return.py
@@ -14,14 +14,14 @@ class ReturnPicking(models.TransientModel):
('id', '=', res['picking_id']),
])
- sale_id = stock_picking.group_id.sale_id
- if not stock_picking.approval_return_status == 'approved' and sale_id.invoice_ids:
+ # sale_id = stock_picking.group_id.sale_id
+ if not stock_picking.approval_return_status == 'approved':
raise UserError('Harus Approval Accounting AR untuk melakukan Retur')
- purchase = self.env['purchase.order'].search([
- ('name', '=', stock_picking.group_id.name),
- ])
- if not stock_picking.approval_return_status == 'approved' and purchase.invoice_ids:
- raise UserError('Harus Approval Accounting AP untuk melakukan Retur')
+ # purchase = self.env['purchase.order'].search([
+ # ('name', '=', stock_picking.group_id.name),
+ # ])
+ # if not stock_picking.approval_return_status == 'approved' and purchase.invoice_ids:
+ # raise UserError('Harus Approval Accounting AP untuk melakukan Retur')
return res \ No newline at end of file
diff --git a/indoteknik_custom/models/user_company_request.py b/indoteknik_custom/models/user_company_request.py
index 4a62400f..af8a86ba 100644
--- a/indoteknik_custom/models/user_company_request.py
+++ b/indoteknik_custom/models/user_company_request.py
@@ -64,6 +64,7 @@ class UserCompanyRequest(models.Model):
user.parent_name = self.user_input
is_approve = vals.get('is_approve')
is_internal_input = vals.get('internal_input')
+ is_company_id = vals.get('user_company_id')
if self.is_approve and is_approve:
raise UserError('Tidak dapat mengubah approval yang sudah diisi')
@@ -71,20 +72,35 @@ class UserCompanyRequest(models.Model):
if self.user_company_id.nama_wajib_pajak == self.user_company_id.name:
self.user_company_id.nama_wajib_pajak = is_internal_input
self.user_company_id.name = is_internal_input
+ user_company_id = []
+ if is_company_id:
+ user_company_id = request.env['res.partner'].search([('id', '=', is_company_id)], limit=1)
+ # self.user_company_id.customer_type = self.similar_company_ids.customer_type
+ # self.user_company_id.npwp = self.similar_company_ids.npwp
+ # self.user_company_id.sppkp = self.similar_company_ids.sppkp
+ # self.user_company_id.nama_wajib_pajak = self.similar_company_ids.nama_wajib_pajak
+ # self.user_company_id.alamat_lengkap_text = self.similar_company_ids.alamat_lengkap_text
+ # self.user_company_id.industry_id = self.similar_company_ids.industry_id
+ # self.user_company_id.company_type_id = self.similar_company_ids.company_type_id
+ # self.user_company_id.user_id = self.similar_company_ids.user_id
+ # self.user_company_id.property_account_receivable_id = self.similar_company_ids.property_account_receivable_id
+ # self.user_company_id.property_account_payable_id = self.similar_company_ids.property_account_payable_id
if not self.is_approve and is_approve:
if is_approve == 'approved':
- self.user_id.parent_id = vals.get('user_company_id') if vals.get('user_company_id') else 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_id.parent_id = user_company_id if user_company_id else self.user_company_id
+ self.user_id.customer_type = user_company_id.customer_type if user_company_id else self.user_company_id.customer_type
+ self.user_id.npwp = user_company_id.npwp if user_company_id else self.user_company_id.npwp
+ self.user_id.sppkp = user_company_id.sppkp if user_company_id else self.user_company_id.sppkp
+ self.user_id.nama_wajib_pajak = user_company_id.nama_wajib_pajak if user_company_id else self.user_company_id.nama_wajib_pajak
+ self.user_id.alamat_lengkap_text = user_company_id.alamat_lengkap_text if user_company_id else self.user_company_id.alamat_lengkap_text
+ self.user_id.industry_id = user_company_id.industry_id.id if user_company_id else self.user_company_id.industry_id
+ self.user_id.company_type_id = user_company_id.company_type_id.id if user_company_id else self.user_company_id.company_type_id
+ self.user_id.user_id = user_company_id.user_id if user_company_id else self.user_company_id.user_id
+ self.user_id.property_account_receivable_id = user_company_id.property_account_receivable_id if user_company_id else self.user_company_id.property_account_receivable_id
+ self.user_id.property_account_payable_id = user_company_id.property_account_payable_id if user_company_id else self.user_company_id.property_account_payable_id
+ self.user_id.property_payment_term_id = user_company_id.property_payment_term_id if user_company_id else self.user_company_id.property_payment_term_id
+ self.user_id.property_supplier_payment_term_id = user_company_id.property_supplier_payment_term_id if user_company_id else self.user_company_id.property_supplier_payment_term_id
self.user_company_id.active = True
user.send_company_switch_approve_mail() if vals.get('is_switch_account') == True else user.send_company_request_approve_mail()
else:
diff --git a/indoteknik_custom/models/user_pengajuan_tempo.py b/indoteknik_custom/models/user_pengajuan_tempo.py
new file mode 100644
index 00000000..0fdcdbeb
--- /dev/null
+++ b/indoteknik_custom/models/user_pengajuan_tempo.py
@@ -0,0 +1,134 @@
+from odoo import models, fields, api
+from datetime import datetime, timedelta
+
+
+# class IrAttachment(models.Model):
+# _inherit = 'ir.attachment'
+#
+# @api.model
+# def create(self, vals):
+# attachment = super(IrAttachment, self).create(vals)
+# if attachment:
+# base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+# attachment.url = f"/web/content/{attachment.id}"
+# return attachment
+
+
+class UserPengajuanTempo(models.Model):
+ _name = 'user.pengajuan.tempo'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ partner_id = fields.Char()
+ _description = 'User Pengajuan Tempo'
+
+ name = fields.Char(string='Name')
+
+ # informasi perusahaan
+ # name_tempo = fields.Many2one(
+ # 'res.partner', string='Nama Perusahaan',
+ # 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
+ # )
+ name_tempo = fields.Many2one(
+ 'res.partner', string='Nama Perusahaan',
+ tracking=True, # Menambahkan tracking=True
+ )
+ user_id = fields.Many2one('res.users', string='User')
+ industry_id_tempo = fields.Many2one('res.partner.industry', 'Customer Industry', readonly=True)
+ street_tempo = fields.Char(string="Alamat Perusahaan")
+ state_id_tempo = fields.Many2one('res.country.state', string='State')
+ city_id_tempo = fields.Many2one('vit.kota', string='City')
+ district_id_tempo = fields.Many2one('vit.kecamatan', string='Kecamatan')
+ subDistrict_id_tempo = fields.Many2one('vit.kelurahan', string='Kelurahan')
+ zip_tempo = fields.Char(string="Zip")
+ mobile_tempo = fields.Char(string="No. Telfon Perusahaan")
+ bank_name_tempo = fields.Char(string="Nama Bank")
+ account_name_tempo = fields.Char(string="Nama Rekening")
+ account_number_tempo = fields.Char(string="Nomor Rekening Bank")
+ website_tempo = fields.Char(string='Website')
+ portal = fields.Boolean(string='Portal Website')
+ bersedia = fields.Char(string='Website')
+ estimasi_tempo = fields.Char(string='Estimasi Pembelian Pertahun')
+ tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo')
+ tempo_limit = fields.Char(string='Limit Tempo')
+ category_produk_ids = fields.Many2many('product.public.category', string='Kategori Produk yang Digunakan', domain=lambda self: self._get_default_category_domain())
+
+ @api.model
+ def _get_default_category_domain(self):
+ return [('parent_id', '=', False)]
+
+ # Kontak Perusahaan
+ direktur_tittle = fields.Char(string='tittle Direktur')
+ direktur_name = fields.Char(string='Nama Lengkap Direktur')
+ direktur_mobile = fields.Char(string='No. Telpon Direktur')
+ direktur_email = fields.Char(string='Email Direktur')
+ purchasing_tittle = fields.Char(string='tittle Purchasing')
+ purchasing_name = fields.Char(string='Nama Purchasing')
+ purchasing_mobile = fields.Char(string='No. Telpon Purchasing')
+ purchasing_email = fields.Char(string='Email Purchasing')
+ finance_tittle = fields.Char(string='tittle Finance')
+ finance_name = fields.Char(string='Nama Finance')
+ finance_mobile = fields.Char(string='No. Telpon Finance')
+ finance_email = fields.Char(string='Email Finance')
+
+ # Pengiriman
+ pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang')
+ pic_name = fields.Char(string='Nama PIC Penerimaan Barang')
+ street_pengiriman = fields.Char(string="Alamat Perusahaan")
+ state_id_pengiriman = fields.Many2one('res.country.state', string='State')
+ city_id_pengiriman = fields.Many2one('vit.kota', string='City')
+ district_id_pengiriman = fields.Many2one('vit.kecamatan', string='Kecamatan')
+ subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan')
+ zip_pengiriman = fields.Char(string="Zip")
+ invoice_pic_tittle = fields.Char(string='Tittle PIC Penerimaan Invoice')
+ invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice')
+ street_invoice = fields.Char(string="Alamat Perusahaan")
+ state_id_invoice = fields.Many2one('res.country.state', string='State')
+ city_id_invoice = fields.Many2one('vit.kota', string='City')
+ district_id_invoice = fields.Many2one('vit.kecamatan', string='Kecamatan')
+ subDistrict_id_invoice = fields.Many2one('vit.kelurahan', string='Kelurahan')
+ zip_invoice = fields.Char(string="Zip")
+ tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice')
+ jadwal_bayar = fields.Char(string='Jadwal Pembayaran')
+ dokumen_pengiriman = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang')
+ dokumen_kirim_input = fields.Char(string='Dokumen lain yang diterima saat pengiriman barang')
+ dokumen_pengiriman_input = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang')
+ dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice')
+ is_same_address = fields.Boolean(string="Same Address pengiriman invoicr dan alamat pengiriman barang")
+ is_same_address_street = fields.Boolean(string="Same Address pengiriman barang dan alamat bisnis")
+
+ # Referensi
+ supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers")
+
+ #Dokumen
+ dokumen_nib = fields.Many2many('ir.attachment', 'pengajuan_dokumen_nib_rel', string="NIB", tracking=True, track_visibility="onchange")
+ dokumen_npwp = fields.Many2many('ir.attachment', 'pengajuan_dokumen_npwp_rel', string="NPWP Perusahaan", tracking=True)
+ dokumen_sppkp = fields.Many2many('ir.attachment', 'pengajuan_dokumen_sppkp_rel', string="SPPKP Perusahaan", tracking=True)
+ dokumen_siup = fields.Many2many('ir.attachment', 'pengajuan_dokumen_siup_rel', string="SIUP Perusahaan", tracking=True)
+ dokumen_tdp = fields.Many2many('ir.attachment', 'pengajuan_dokumen_tdp_rel', string="TDP Perusahaan", tracking=True)
+ dokumen_skdp = fields.Many2many('ir.attachment', 'pengajuan_dokumen_skdp_rel', string="SKDP Perusahaan", tracking=True)
+ dokumen_skt = fields.Many2many('ir.attachment', 'pengajuan_dokumen_skt_rel', string="SKT Perusahaan", tracking=True)
+ dokumen_akta_perubahan = fields.Many2many('ir.attachment', 'pengajuan_dokumen_akta_perubahan_rel',
+ string="Akta Perubahan", tracking=True)
+ dokumen_ktp_dirut = fields.Many2many('ir.attachment', 'pengajuan_dokumen_ktp_dirut_rel',
+ string="KTP Dirut/Direktur", tracking=True)
+ dokumen_akta_pendirian = fields.Many2many('ir.attachment', 'pengajuan_dokumen_angkta_pendirian_rel',
+ string="Akta Pendirian", tracking=True)
+ dokumen_laporan_keuangan = fields.Many2many('ir.attachment', 'pengajuan_dokumen_laporan_keuangan_rel',
+ string="Laporan Keuangan", tracking=True)
+ dokumen_foto_kantor = fields.Many2many('ir.attachment', 'pengajuan_dokumen_foto_kantor_rel',
+ string=" Foto Kantor (Tampak Depan)", tracking=True)
+ dokumen_tempat_bekerja = fields.Many2many('ir.attachment', 'pengajuan_dokumen_tempat_bekerja_rel',
+ string="Tempat Bekerja", tracking=True)
+
+ @api.depends('name', 'name_tempo')
+ def name_get(self):
+ result = []
+ for record in self:
+ if record.name_tempo:
+ display_name = f"DETAIL FORM TEMPO - {record.name_tempo.name}"
+ else:
+ display_name = "DETAIL FORM TEMPO"
+ result.append((record.id, display_name))
+ return result
diff --git a/indoteknik_custom/models/user_pengajuan_tempo_line.py b/indoteknik_custom/models/user_pengajuan_tempo_line.py
new file mode 100644
index 00000000..db519ed6
--- /dev/null
+++ b/indoteknik_custom/models/user_pengajuan_tempo_line.py
@@ -0,0 +1,12 @@
+from odoo import models, fields
+
+
+class PengajuanTempoLine(models.Model):
+ _name = 'user.pengajuan.tempo.line'
+
+ # Fields untuk tabel supplier
+ name_supplier = fields.Char(string="Nama Supplier")
+ pic_name = fields.Char(string="PIC")
+ phone = fields.Char(string="Telepon")
+ tempo_duration = fields.Char(string="Durasi Tempo")
+ credit_limit = fields.Char(string="Credit Limit") \ No newline at end of file
diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py
new file mode 100644
index 00000000..f5261cd4
--- /dev/null
+++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py
@@ -0,0 +1,630 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import UserError
+from odoo.http import request
+
+class RejectReasonWizard(models.TransientModel):
+ _name = 'reject.reason.wizard'
+ _description = 'Wizard for Reject Reason'
+
+ request_id = fields.Many2one('user.pengajuan.tempo.request', string='Request')
+ reason_reject = fields.Text(string='Reason for Rejection', required=True)
+
+ def confirm_reject(self):
+ tempo = self.request_id
+ if tempo:
+ tempo.write({'reason_reject': self.reason_reject})
+ tempo.state_tempo = 'reject'
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_reject')
+ template.send_mail(tempo.id, force_send=True)
+ return {'type': 'ir.actions.act_window_close'}
+class ConfirmApprovalWizard(models.TransientModel):
+ _name = 'confirm.approval.wizard'
+ _description = 'Wizard Konfirmasi Approval'
+
+ tempo_id = fields.Many2one('user.pengajuan.tempo.request', string='Tempo', required=True)
+
+ def confirm_approval(self):
+ tempo = self.tempo_id
+ if tempo.state_tempo == 'draft':
+ tempo.state_tempo = 'approval_sales'
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_approved_by_sales')
+ template.send_mail(tempo.id, force_send=True)
+ elif tempo.state_tempo == 'approval_sales':
+ tempo.state_tempo = 'approval_finance'
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_to_aprove_director')
+ template.send_mail(tempo.id, force_send=True)
+ elif tempo.state_tempo == 'approval_finance':
+ tempo.state_tempo = 'approval_director'
+
+
+
+class UserPengajuanTempoRequest(models.Model):
+ _name = 'user.pengajuan.tempo.request'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _rec_name = 'user_id'
+
+ user_id = fields.Many2one('res.partner', string='User')
+ user_company_id = fields.Many2one('res.partner', string='Company')
+ pengajuan_tempo_id = fields.Many2one('user.pengajuan.tempo', string='Form Tempo')
+ tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo', tracking=3, domain=[('id', 'in', [24, 25, 29, 32])])
+ tempo_limit = fields.Integer(string='Limit Tempo', tracking=3)
+ state_tempo = fields.Selection([
+ ('draft', 'Pengajuan Tempo'),
+ ('approval_sales', 'Approved by Sales Manager'),
+ ('approval_finance', 'Approved by Finance'),
+ ('approval_director', 'Approved by Director'),
+ ('reject', 'Rejected'),
+ ], string='Status', readonly=True, copy=False, index=True, track_visibility='onchange', default='draft')
+ reason_reject = fields.Char(string='Limit Tempo')
+
+ # informasi perusahaan
+ name_tempo = fields.Many2one('res.partner', string='Nama Perusahaan', related='pengajuan_tempo_id.name_tempo', store=True, tracking=True, readonly=False)
+ industry_id_tempo = fields.Many2one('res.partner.industry', 'Customer Industry', related='pengajuan_tempo_id.industry_id_tempo',store=True, tracking=True, readonly=False)
+ street_tempo = fields.Char(string="Alamat Perusahaan", related='pengajuan_tempo_id.street_tempo', store=True, tracking=True, readonly=False)
+ state_id_tempo = fields.Many2one('res.country.state', string='State', related='pengajuan_tempo_id.state_id_tempo', store=True, tracking=True, readonly=False)
+ city_id_tempo = fields.Many2one('vit.kota', string='City', related='pengajuan_tempo_id.city_id_tempo', store=True, tracking=True, readonly=False)
+ zip_tempo = fields.Char(string="Zip", related='pengajuan_tempo_id.zip_tempo', store=True, tracking=True, readonly=False)
+ mobile_tempo = fields.Char(string="No. Telfon Perusahaan", related='pengajuan_tempo_id.mobile_tempo', store=True, tracking=True, readonly=False)
+ bank_name_tempo = fields.Char(string="Nama Bank", related='pengajuan_tempo_id.bank_name_tempo', store=True, tracking=True, readonly=False)
+ account_name_tempo = fields.Char(string="Nama Rekening", related='pengajuan_tempo_id.account_name_tempo', store=True, tracking=True, readonly=False)
+ account_number_tempo = fields.Char(string="Nomor Rekening Bank", related='pengajuan_tempo_id.account_number_tempo', store=True, tracking=True, readonly=False)
+ website_tempo = fields.Char(string='Website', related='pengajuan_tempo_id.website_tempo', store=True, tracking=True, readonly=False)
+ portal = fields.Boolean(string='Portal Website', related='pengajuan_tempo_id.portal', store=True, tracking=True, readonly=False)
+ estimasi_tempo = fields.Char(string='Estimasi Pembelian Pertahun', related='pengajuan_tempo_id.estimasi_tempo', store=True, tracking=True, readonly=False)
+ tempo_duration_origin = fields.Many2one('account.payment.term', string='Durasi Tempo', related='pengajuan_tempo_id.tempo_duration', store=True, tracking=True, readonly=False, domain=[('id', 'in', [24, 25, 29, 32])])
+ tempo_limit_origin = fields.Char(string='Limit Tempo', related='pengajuan_tempo_id.tempo_limit' , store=True, tracking=True, readonly=False)
+ category_produk_ids = fields.Many2many('product.public.category', string='Kategori Produk yang Digunakan', related='pengajuan_tempo_id.category_produk_ids', readonly=False)
+
+ # Kontak Perusahaan
+ direktur_name = fields.Char(string='Nama Lengkap Direktur', related='pengajuan_tempo_id.direktur_name', store=True, readonly=False)
+ direktur_mobile = fields.Char(string='No. Telpon Direktur', related='pengajuan_tempo_id.direktur_mobile', store=True, readonly=False)
+ direktur_email = fields.Char(string='Email Direktur', related='pengajuan_tempo_id.direktur_email', store=True, readonly=False)
+ purchasing_name = fields.Char(string='Nama Purchasing', related='pengajuan_tempo_id.purchasing_name', store=True, readonly=False)
+ purchasing_mobile = fields.Char(string='No. Telpon Purchasing', related='pengajuan_tempo_id.purchasing_mobile', store=True, readonly=False)
+ purchasing_email = fields.Char(string='Email Purchasing', related='pengajuan_tempo_id.purchasing_email', store=True, readonly=False)
+ finance_name = fields.Char(string='Nama Finance', related='pengajuan_tempo_id.finance_name', store=True, readonly=False)
+ finance_mobile = fields.Char(string='No. Telpon Finance', related='pengajuan_tempo_id.finance_mobile', store=True, readonly=False)
+ finance_email = fields.Char(string='Email Finance', related='pengajuan_tempo_id.finance_email', store=True, readonly=False)
+
+ # Pengiriman
+ pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_tittle', store=True, readonly=False)
+ pic_name = fields.Char(string='Nama PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_name', store=True, readonly=False)
+ street_pengiriman = fields.Char(string="Alamat Perusahaan", related='pengajuan_tempo_id.street_pengiriman', store=True, readonly=False)
+ state_id_pengiriman = fields.Many2one('res.country.state', string='State', related='pengajuan_tempo_id.state_id_pengiriman', store=True, readonly=False)
+ city_id_pengiriman = fields.Many2one('vit.kota', string='City', related='pengajuan_tempo_id.city_id_pengiriman', store=True, readonly=False)
+ district_id_pengiriman = fields.Many2one('vit.kecamatan', string='Kecamatan',related='pengajuan_tempo_id.district_id_pengiriman', store=True, readonly=False)
+ subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan', related='pengajuan_tempo_id.subDistrict_id_pengiriman', store=True, readonly=False)
+ zip_pengiriman = fields.Char(string="Zip", related='pengajuan_tempo_id.zip_pengiriman', store=True, readonly=False)
+ invoice_pic_tittle = fields.Char(string='Tittle PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_tittle', store=True, readonly=False)
+ invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic', store=True, readonly=False)
+ street_invoice = fields.Char(string="Alamat Perusahaan", related='pengajuan_tempo_id.street_invoice', store=True, readonly=False)
+ state_id_invoice = fields.Many2one('res.country.state', string='State', related='pengajuan_tempo_id.state_id_invoice', store=True, readonly=False)
+ city_id_invoice = fields.Many2one('vit.kota', string='City', related='pengajuan_tempo_id.city_id_invoice', store=True, readonly=False)
+ district_id_invoice = fields.Many2one('vit.kecamatan', string='Kecamatan', related='pengajuan_tempo_id.district_id_invoice', store=True, readonly=False)
+ subDistrict_id_invoice = fields.Many2one('vit.kelurahan', string='Kelurahan', related='pengajuan_tempo_id.subDistrict_id_invoice', store=True, readonly=False)
+ zip_invoice = fields.Char(string="Zip", related='pengajuan_tempo_id.zip_invoice', store=True, readonly=False)
+ tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice', related='pengajuan_tempo_id.tukar_invoice', store=True, readonly=False)
+ jadwal_bayar = fields.Char(string='Jadwal Pembayaran', related='pengajuan_tempo_id.jadwal_bayar', store=True, readonly=False)
+ dokumen_pengiriman = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang', related='pengajuan_tempo_id.dokumen_pengiriman', store=True, readonly=False)
+ dokumen_pengiriman_input = fields.Char(string='Dokumen yang dibawa saat pengiriman barang', related='pengajuan_tempo_id.dokumen_pengiriman_input', store=True, readonly=False)
+ dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice', related='pengajuan_tempo_id.dokumen_invoice', store=True, readonly=False)
+ is_same_address = fields.Boolean(string="Same Address pengiriman invoicr dan alamat pengiriman barang", related='pengajuan_tempo_id.is_same_address', store=True, readonly=False)
+ is_same_address_street = fields.Boolean(string="Same Address pengiriman barang dan alamat bisnis", related='pengajuan_tempo_id.is_same_address_street', store=True, readonly=False)
+
+ #Referensi
+ supplier_ids = fields.Many2many('user.pengajuan.tempo.line',related='pengajuan_tempo_id.supplier_ids', string="Suppliers", readonly=False)
+
+ # Dokumen
+ dokumen_npwp = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_npwp_rel',
+ string="NPWP Perusahaan",
+ related='pengajuan_tempo_id.dokumen_npwp',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_sppkp = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_sppkp_rel',
+ string="SPPKP Perusahaan",
+ related='pengajuan_tempo_id.dokumen_sppkp',
+ readonly=False,
+ tracking=3
+ )
+ dokumen_nib = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_nib_rel',
+ string="NIB",
+ related='pengajuan_tempo_id.dokumen_nib',
+ readonly=False,
+ tracking=3,
+ track_visibility="onchange"
+ )
+ dokumen_siup = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_siup_rel',
+ string="SIUP",
+ related='pengajuan_tempo_id.dokumen_siup',
+ readonly=False,
+ tracking=3,
+ track_visibility="onchange"
+ )
+
+ dokumen_tdp = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_tdp_rel',
+ string="TDP",
+ related='pengajuan_tempo_id.dokumen_tdp',
+ readonly=False,
+ tracking=3,
+ track_visibility="onchange"
+ )
+ dokumen_skdp = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_skdp_rel',
+ string="SKDP",
+ related='pengajuan_tempo_id.dokumen_skdp',
+ readonly=False,
+ tracking=3,
+ track_visibility="onchange"
+ )
+
+ dokumen_skt = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_skt_rel',
+ string="SKT",
+ related='pengajuan_tempo_id.dokumen_skt',
+ readonly=False,
+ tracking=3,
+ track_visibility="onchange"
+ )
+
+ dokumen_akta_perubahan = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_akta_perubahan_rel',
+ string="Akta Perubahan",
+ related='pengajuan_tempo_id.dokumen_akta_perubahan',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_ktp_dirut = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_ktp_dirut_rel',
+ string="KTP Dirut/Direktur",
+ related='pengajuan_tempo_id.dokumen_ktp_dirut',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_akta_pendirian = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_angkta_pendirian_rel',
+ string="Akta Pendirian",
+ related='pengajuan_tempo_id.dokumen_akta_pendirian',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_laporan_keuangan = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_laporan_keuangan_rel',
+ string="Laporan Keuangan",
+ related='pengajuan_tempo_id.dokumen_laporan_keuangan',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_foto_kantor = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_foto_kantor_rel',
+ string="Foto Kantor (Tampak Depan)",
+ related='pengajuan_tempo_id.dokumen_foto_kantor',
+ readonly=False,
+ tracking=3
+ )
+
+ dokumen_tempat_bekerja = fields.Many2many(
+ 'ir.attachment',
+ 'pengajuan_dokumen_tempat_bekerja_rel',
+ string="Tempat Bekerja",
+ related='pengajuan_tempo_id.dokumen_tempat_bekerja',
+ readonly=False,
+ tracking=3
+ )
+
+ @api.onchange('name_tempo', 'industry_id_tempo', 'street_tempo', 'state_id_tempo', 'city_id_tempo', 'zip_tempo',
+ 'mobile_tempo', 'bank_name_tempo', 'account_name_tempo', 'account_number_tempo', 'website_tempo',
+ 'estimasi_tempo', 'tempo_duration_origin', 'tempo_limit_origin', 'category_produk_ids')
+ def _onchange_related_fields(self):
+ if self.pengajuan_tempo_id:
+ # Perbarui nilai di pengajuan_tempo_id
+ self.pengajuan_tempo_id.name_tempo = self.name_tempo
+ self.pengajuan_tempo_id.industry_id_tempo = self.industry_id_tempo
+ self.pengajuan_tempo_id.street_tempo = self.street_tempo
+ self.pengajuan_tempo_id.state_id_tempo = self.state_id_tempo
+ self.pengajuan_tempo_id.city_id_tempo = self.city_id_tempo
+ self.pengajuan_tempo_id.zip_tempo = self.zip_tempo
+ self.pengajuan_tempo_id.mobile_tempo = self.mobile_tempo
+ self.pengajuan_tempo_id.bank_name_tempo = self.bank_name_tempo
+ self.pengajuan_tempo_id.account_name_tempo = self.account_name_tempo
+ self.pengajuan_tempo_id.account_number_tempo = self.account_number_tempo
+ self.pengajuan_tempo_id.website_tempo = self.website_tempo
+ self.pengajuan_tempo_id.portal = self.portal
+ self.pengajuan_tempo_id.estimasi_tempo = self.estimasi_tempo
+ self.pengajuan_tempo_id.tempo_duration = self.tempo_duration_origin
+ self.pengajuan_tempo_id.tempo_limit = self.tempo_limit_origin
+ self.pengajuan_tempo_id.category_produk_ids = self.category_produk_ids
+
+ @api.onchange('direktur_name', 'direktur_mobile', 'direktur_email', 'purchasing_name', 'purchasing_mobile',
+ 'purchasing_email', 'finance_name', 'finance_mobile', 'finance_email')
+ def _onchange_related_fields_kontak(self):
+ if self.pengajuan_tempo_id:
+ # Perbarui nilai di pengajuan_tempo_id
+ self.pengajuan_tempo_id.direktur_name = self.direktur_name
+ self.pengajuan_tempo_id.direktur_mobile = self.direktur_mobile
+ self.pengajuan_tempo_id.direktur_email = self.direktur_email
+ self.pengajuan_tempo_id.purchasing_name = self.purchasing_name
+ self.pengajuan_tempo_id.purchasing_mobile = self.purchasing_mobile
+ self.pengajuan_tempo_id.purchasing_email = self.purchasing_email
+ self.pengajuan_tempo_id.finance_name = self.finance_name
+ self.pengajuan_tempo_id.finance_mobile = self.finance_mobile
+ self.pengajuan_tempo_id.finance_email = self.finance_email
+
+ @api.onchange('pic_tittle', 'pic_name', 'street_pengiriman', 'state_id_pengiriman', 'city_id_pengiriman',
+ 'zip_pengiriman', 'district_id_pengiriman', 'subDistrict_id_pengiriman'
+ 'invoice_pic_tittle', 'invoice_pic', 'street_invoice', 'state_id_invoice', 'city_id_invoice',
+ 'district_id_invoice', 'subDistrict_id_invoice', 'zip_invoice',
+ 'tukar_invoice', 'jadwal_bayar', 'dokumen_pengiriman', 'dokumen_pengiriman_input', 'dokumen_invoice',
+ 'is_same_address', 'is_same_address_street')
+ def _onchange_related_fields_pengiriman(self):
+ if self.pengajuan_tempo_id:
+ # Perbarui nilai di pengajuan_tempo_id
+ self.pengajuan_tempo_id.pic_tittle = self.pic_tittle
+ self.pengajuan_tempo_id.pic_name = self.pic_name
+ self.pengajuan_tempo_id.street_pengiriman = self.street_pengiriman
+ self.pengajuan_tempo_id.state_id_pengiriman = self.state_id_pengiriman
+ self.pengajuan_tempo_id.city_id_pengiriman = self.city_id_pengiriman
+ self.pengajuan_tempo_id.district_id_pengiriman = self.district_id_pengiriman
+ self.pengajuan_tempo_id.subDistrict_id_pengiriman = self.subDistrict_id_pengiriman
+ self.pengajuan_tempo_id.zip_pengiriman = self.zip_pengiriman
+ self.pengajuan_tempo_id.invoice_pic_tittle = self.invoice_pic_tittle
+ self.pengajuan_tempo_id.invoice_pic = self.invoice_pic
+ self.pengajuan_tempo_id.street_invoice = self.street_invoice
+ self.pengajuan_tempo_id.state_id_invoice = self.state_id_invoice
+ self.pengajuan_tempo_id.city_id_invoice = self.city_id_invoice
+ self.pengajuan_tempo_id.district_id_invoice = self.district_id_invoice
+ self.pengajuan_tempo_id.subDistrict_id_invoice = self.subDistrict_id_invoice
+ self.pengajuan_tempo_id.zip_invoice = self.zip_invoice
+ self.pengajuan_tempo_id.tukar_invoice = self.tukar_invoice
+ self.pengajuan_tempo_id.jadwal_bayar = self.jadwal_bayar
+ self.pengajuan_tempo_id.dokumen_pengiriman = self.dokumen_pengiriman
+ self.pengajuan_tempo_id.dokumen_pengiriman_input = self.dokumen_pengiriman_input
+ self.pengajuan_tempo_id.dokumen_invoice = self.dokumen_invoice
+ self.pengajuan_tempo_id.is_same_address = self.is_same_address
+ self.pengajuan_tempo_id.is_same_address_street = self.is_same_address_street
+
+ @api.onchange('supplier_ids')
+ def _onchange_supplier_ids(self):
+ if self.pengajuan_tempo_id:
+ self.pengajuan_tempo_id.supplier_ids = self.supplier_ids
+
+ @api.onchange('dokumen_nib', 'dokumen_npwp', 'dokumen_sppkp', 'dokumen_akta_perubahan',
+ 'dokumen_ktp_dirut', 'dokumen_akta_pendirian', 'dokumen_laporan_keuangan',
+ 'dokumen_foto_kantor', 'dokumen_tempat_bekerja', 'dokumen_skdp', 'dokumen_skt', 'dokumen_siup',
+ 'dokumen_tdp')
+ def _onchange_related_fields_dokumen(self):
+ if self.pengajuan_tempo_id:
+ # Perbarui nilai di pengajuan_tempo_id
+ self.pengajuan_tempo_id.dokumen_nib = self.dokumen_nib
+ self.pengajuan_tempo_id.dokumen_siup = self.dokumen_siup
+ self.pengajuan_tempo_id.dokumen_tdp = self.dokumen_tdp
+ self.pengajuan_tempo_id.dokumen_skdp = self.dokumen_skdp
+ self.pengajuan_tempo_id.dokumen_skt = self.dokumen_skt
+ self.pengajuan_tempo_id.dokumen_npwp = self.dokumen_npwp
+ self.pengajuan_tempo_id.dokumen_sppkp = self.dokumen_sppkp
+ self.pengajuan_tempo_id.dokumen_akta_perubahan = self.dokumen_akta_perubahan
+ self.pengajuan_tempo_id.dokumen_ktp_dirut = self.dokumen_ktp_dirut
+ self.pengajuan_tempo_id.dokumen_akta_pendirian = self.dokumen_akta_pendirian
+ self.pengajuan_tempo_id.dokumen_laporan_keuangan = self.dokumen_laporan_keuangan
+ self.pengajuan_tempo_id.dokumen_foto_kantor = self.dokumen_foto_kantor
+ self.pengajuan_tempo_id.dokumen_tempat_bekerja = self.dokumen_tempat_bekerja
+
+ @api.onchange('tempo_duration')
+ def _tempo_duration_change(self, vals):
+ for tempo in self:
+ if tempo.env.user.id not in (7, 377, 12182):
+ raise UserError("Durasi tempo hanya bisa di ubah oleh Sales Manager atau Direktur")
+
+ @api.onchange('tempo_limit')
+ def _onchange_tempo_limit(self):
+ for tempo in self:
+ if tempo.env.user.id not in (7, 377, 12182):
+ raise UserError("Limit tempo hanya bisa diubah oleh Sales Manager atau Direktur")
+ def button_approve(self):
+ for tempo in self:
+ if not self.tempo_limit:
+ raise UserError("Limit Tempo harus di isi terlebih dahulu")
+ if tempo.state_tempo == 'draft':
+ if tempo.env.user.id in (688, 28, 7):
+ raise UserError("Pengajuan tempo harus di approve oleh sales manager terlebih dahulu")
+ else:
+ if tempo.env.user.id not in (377, 12182):
+ # if tempo.env.user.id != 12182:
+ raise UserError("Pengajuan tempo hanya bisa di approve oleh sales manager")
+ else:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Konfirmasi Approve',
+ 'res_model': 'confirm.approval.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_tempo_id': tempo.id,
+ },
+ }
+
+ elif tempo.state_tempo == 'approval_sales':
+ if tempo.env.user.id == 7:
+ raise UserError("Pengajuan tempo harus di approve oleh Finence terlebih dahulu")
+ else:
+ if tempo.env.user.id not in (688, 28, 12182):
+ # if tempo.env.user.id not in (288,28,12182):
+ raise UserError("Pengajuan tempo hanya bisa di approve oleh Finence")
+ else:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Konfirmasi Approve',
+ 'res_model': 'confirm.approval.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_tempo_id': tempo.id,
+ },
+ }
+
+ elif tempo.state_tempo == 'approval_finance':
+ if tempo.env.user.id != 7:
+ # if tempo.env.user.id != 12182:
+ raise UserError("Pengajuan tempo hanya bisa di approve oleh Direktur")
+ else:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Konfirmasi Approve',
+ 'res_model': 'confirm.approval.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_tempo_id': tempo.id,
+ },
+ }
+
+ def button_reject(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': _('Reject Reason'),
+ 'res_model': 'reject.reason.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {'default_request_id': self.id},
+ }
+
+ def write(self, vals):
+ is_approve = True if self.state_tempo == 'approval_director' or vals.get('state_tempo') == 'approval_director' else False
+ # if self.is_approve and is_approve:
+ # raise UserError('Tidak dapat mengubah approval yang sudah diisi')
+ limit_tempo = ''
+ if vals.get('tempo_limit'):
+ limit_tempo = vals.get('tempo_limit')
+ elif self.tempo_limit:
+ limit_tempo = self.tempo_limit
+ else:
+ limit_tempo = self.pengajuan_tempo_id.tempo_limit
+
+ tempo_duration = ''
+ if vals.get('tempo_duration'):
+ tempo_duration = vals.get('tempo_duration')
+ elif self.tempo_duration:
+ tempo_duration = self.tempo_duration
+ else:
+ tempo_duration = self.pengajuan_tempo_id.tempo_duration
+ if is_approve:
+ self.pengajuan_tempo_id.partner_id = self.user_id.id
+ # informasi perusahaan
+ self.user_company_id.name_tempo = self.pengajuan_tempo_id.name_tempo
+ self.user_company_id.industry_id_tempo = self.pengajuan_tempo_id.industry_id_tempo
+ self.user_company_id.street_tempo = self.pengajuan_tempo_id.street_tempo
+ self.user_company_id.state_id_tempo = self.pengajuan_tempo_id.state_id_tempo
+ self.user_company_id.city_id_tempo = self.pengajuan_tempo_id.city_id_tempo
+ self.user_company_id.zip_tempo = self.pengajuan_tempo_id.zip_tempo
+ self.user_company_id.mobile_tempo = self.pengajuan_tempo_id.mobile_tempo
+ self.user_company_id.bank_name_tempo = self.pengajuan_tempo_id.bank_name_tempo
+ self.user_company_id.account_name_tempo = self.pengajuan_tempo_id.account_name_tempo
+ self.user_company_id.account_number_tempo = self.pengajuan_tempo_id.account_number_tempo
+ self.user_company_id.website_tempo = self.pengajuan_tempo_id.website_tempo
+ self.user_company_id.portal = self.pengajuan_tempo_id.portal
+ self.user_company_id.estimasi_tempo = self.pengajuan_tempo_id.estimasi_tempo
+ self.user_company_id.tempo_duration = tempo_duration.id
+ self.user_company_id.tempo_limit = limit_tempo
+ self.user_company_id.category_produk_ids = self.pengajuan_tempo_id.category_produk_ids
+
+ # Kontak Perusahaan
+ self.user_company_id.direktur_name = self.pengajuan_tempo_id.direktur_name
+ self.user_company_id.direktur_mobile = self.pengajuan_tempo_id.direktur_mobile
+ self.user_company_id.direktur_email = self.pengajuan_tempo_id.direktur_email
+ self.user_company_id.purchasing_name = self.pengajuan_tempo_id.purchasing_name
+ self.user_company_id.purchasing_mobile = self.pengajuan_tempo_id.purchasing_mobile
+ self.user_company_id.purchasing_email = self.pengajuan_tempo_id.purchasing_email
+ self.user_company_id.finance_name = self.pengajuan_tempo_id.finance_name
+ self.user_company_id.finance_mobile = self.pengajuan_tempo_id.finance_mobile
+ self.user_company_id.finance_email = self.pengajuan_tempo_id.finance_email
+
+ # Data untuk kontak baru
+ contacts_data = [
+ {
+ "type": "contact",
+ 'title': 6 if self.pengajuan_tempo_id.direktur_tittle == 'Bpk' else 7,
+ "name": self.pengajuan_tempo_id.direktur_name,
+ "email": self.pengajuan_tempo_id.direktur_email,
+ "phone": self.pengajuan_tempo_id.direktur_mobile,
+ },
+ {
+ "type": "contact",
+ 'title': 6 if self.pengajuan_tempo_id.purchasing_tittle == 'Bpk' else 7,
+ "name": self.pengajuan_tempo_id.purchasing_name,
+ "email": self.pengajuan_tempo_id.purchasing_email,
+ "phone": self.pengajuan_tempo_id.purchasing_mobile,
+ },
+ {
+ "type": "contact",
+ 'title': 6 if self.pengajuan_tempo_id.finance_tittle == 'Bpk' else 7,
+ "name": self.pengajuan_tempo_id.finance_name,
+ "email": self.pengajuan_tempo_id.finance_email,
+ "phone": self.pengajuan_tempo_id.finance_mobile,
+ },
+ {
+ "type": "delivery",
+ "name": self.pengajuan_tempo_id.pic_name,
+ "street": self.pengajuan_tempo_id.street_pengiriman,
+ "state_id": self.pengajuan_tempo_id.state_id_pengiriman.id,
+ "kota_id": self.pengajuan_tempo_id.city_id_pengiriman.id,
+ "kecamatan_id": self.pengajuan_tempo_id.district_id_pengiriman.id,
+ "kelurahan_id": self.pengajuan_tempo_id.subDistrict_id_pengiriman.id,
+ "zip": self.pengajuan_tempo_id.zip_pengiriman,
+ },
+ {
+ "type": "invoice",
+ "name": self.pengajuan_tempo_id.invoice_pic,
+ "street": self.pengajuan_tempo_id.street_invoice,
+ "state_id": self.pengajuan_tempo_id.state_id_invoice.id,
+ "kota_id": self.pengajuan_tempo_id.city_id_invoice.id,
+ "kecamatan_id": self.pengajuan_tempo_id.district_id_invoice.id,
+ "kelurahan_id": self.pengajuan_tempo_id.subDistrict_id_invoice.id,
+ "zip": self.pengajuan_tempo_id.zip_invoice,
+ },
+ ]
+
+ # Buat kontak baru untuk company_id
+ for contact_data in contacts_data:
+ self.env['res.partner'].create({
+ "parent_id": self.user_company_id.id, # Hubungkan ke perusahaan
+ **contact_data, # Tambahkan data kontak
+ })
+
+ # Pengiriman
+ self.user_company_id.pic_name = self.pengajuan_tempo_id.pic_name
+ self.user_company_id.street_pengiriman = self.pengajuan_tempo_id.street_pengiriman
+ self.user_company_id.state_id_pengiriman = self.pengajuan_tempo_id.state_id_pengiriman
+ self.user_company_id.city_id_pengiriman = self.pengajuan_tempo_id.city_id_pengiriman
+ self.user_company_id.district_id_pengiriman = self.pengajuan_tempo_id.district_id_pengiriman
+ self.user_company_id.subDistrict_id_pengiriman = self.pengajuan_tempo_id.subDistrict_id_pengiriman
+ self.user_company_id.zip_pengiriman = self.pengajuan_tempo_id.zip_pengiriman
+ self.user_company_id.invoice_pic = self.pengajuan_tempo_id.invoice_pic
+ self.user_company_id.street_invoice = self.pengajuan_tempo_id.street_invoice
+ self.user_company_id.state_id_invoice = self.pengajuan_tempo_id.state_id_invoice
+ self.user_company_id.city_id_invoice = self.pengajuan_tempo_id.city_id_invoice
+ self.user_company_id.district_id_invoice = self.pengajuan_tempo_id.district_id_invoice
+ self.user_company_id.subDistrict_id_invoice = self.pengajuan_tempo_id.subDistrict_id_invoice
+ self.user_company_id.zip_invoice = self.pengajuan_tempo_id.zip_invoice
+ self.user_company_id.tukar_invoice = self.pengajuan_tempo_id.tukar_invoice
+ self.user_company_id.jadwal_bayar = self.pengajuan_tempo_id.jadwal_bayar
+ self.user_company_id.dokumen_pengiriman = self.pengajuan_tempo_id.dokumen_pengiriman
+ self.user_company_id.dokumen_pengiriman_input = self.pengajuan_tempo_id.dokumen_pengiriman_input
+ self.user_company_id.dokumen_invoice = self.pengajuan_tempo_id.dokumen_invoice
+
+ # Referensi
+ self.user_company_id.supplier_ids = self.pengajuan_tempo_id.supplier_ids
+
+ # Dokumen
+ self.user_company_id.dokumen_npwp = self.pengajuan_tempo_id.dokumen_npwp[0] if self.pengajuan_tempo_id.dokumen_npwp else []
+ if self.user_company_id.dokumen_npwp:
+ self.user_company_id.message_post(body='Dokumen NPWP', attachment_ids=[self.user_company_id.dokumen_npwp.id])
+
+ self.user_company_id.dokumen_sppkp = self.pengajuan_tempo_id.dokumen_sppkp[0] if self.pengajuan_tempo_id.dokumen_sppkp else []
+ if self.user_company_id.dokumen_sppkp:
+ self.user_company_id.message_post(body='Dokumen SPPKP', attachment_ids=[self.user_company_id.dokumen_sppkp.id])
+
+ self.user_company_id.dokumen_nib = self.pengajuan_tempo_id.dokumen_nib[0] if self.pengajuan_tempo_id.dokumen_nib else []
+ if self.user_company_id.dokumen_nib:
+ self.user_company_id.message_post(body='Dokumen NIB', attachment_ids=[self.user_company_id.dokumen_nib.id])
+
+ self.user_company_id.dokumen_siup = self.pengajuan_tempo_id.dokumen_siup[0] if self.pengajuan_tempo_id.dokumen_siup else []
+ if self.user_company_id.dokumen_siup:
+ self.user_company_id.message_post(body='dokumen SIUP', attachment_ids=[self.user_company_id.dokumen_siup.id])
+
+ self.user_company_id.dokumen_tdp = self.pengajuan_tempo_id.dokumen_tdp[0] if self.pengajuan_tempo_id.dokumen_tdp else []
+ if self.user_company_id.dokumen_tdp:
+ self.user_company_id.message_post(body='dokumen TDP', attachment_ids=[self.user_company_id.dokumen_tdp.id])
+
+ self.user_company_id.dokumen_skdp = self.pengajuan_tempo_id.dokumen_skdp[0] if self.pengajuan_tempo_id.dokumen_skdp else []
+ if self.user_company_id.dokumen_skdp:
+ self.user_company_id.message_post(body='dokumen SKDP', attachment_ids=[self.user_company_id.dokumen_skdp.id])
+
+ self.user_company_id.dokumen_skt = self.pengajuan_tempo_id.dokumen_skt[0] if self.pengajuan_tempo_id.dokumen_skt else []
+ if self.user_company_id.dokumen_skt:
+ self.user_company_id.message_post(body='dokumen SKT', attachment_ids=[self.user_company_id.dokumen_skt.id])
+
+ self.user_company_id.dokumen_akta_perubahan = self.pengajuan_tempo_id.dokumen_akta_perubahan[0] if self.pengajuan_tempo_id.dokumen_akta_perubahan else []
+ if self.user_company_id.dokumen_akta_perubahan:
+ self.user_company_id.message_post(body='Dokumen Akta Perubahan',
+ attachment_ids=[self.user_company_id.dokumen_akta_perubahan.id])
+
+ self.user_company_id.dokumen_ktp_dirut = self.pengajuan_tempo_id.dokumen_ktp_dirut[0] if self.pengajuan_tempo_id.dokumen_ktp_dirut else []
+ if self.user_company_id.dokumen_ktp_dirut:
+ self.user_company_id.message_post(body='Dokumen Ktp Dirut',
+ attachment_ids=[self.user_company_id.dokumen_ktp_dirut.id])
+
+ self.user_company_id.dokumen_akta_pendirian = self.pengajuan_tempo_id.dokumen_akta_pendirian[0] if self.pengajuan_tempo_id.dokumen_akta_pendirian else []
+ if self.user_company_id.dokumen_akta_pendirian:
+ self.user_company_id.message_post(body='Dokumen Akta Pendirian',
+ attachment_ids=[self.user_company_id.dokumen_akta_pendirian.id])
+
+ self.user_company_id.dokumen_laporan_keuangan = self.pengajuan_tempo_id.dokumen_laporan_keuangan[0] if self.pengajuan_tempo_id.dokumen_laporan_keuangan else []
+ if self.user_company_id.dokumen_laporan_keuangan:
+ self.user_company_id.message_post(body='Dokumen Laporan Keuangan',
+ attachment_ids=[self.user_company_id.dokumen_laporan_keuangan.id])
+
+ self.user_company_id.dokumen_foto_kantor = self.pengajuan_tempo_id.dokumen_foto_kantor[0] if self.pengajuan_tempo_id.dokumen_foto_kantor else []
+ if self.user_company_id.dokumen_foto_kantor:
+ self.user_company_id.message_post(body='Dokumen Foto Kantor',
+ attachment_ids=[self.user_company_id.dokumen_foto_kantor.id])
+
+ self.user_company_id.dokumen_tempat_bekerja = self.pengajuan_tempo_id.dokumen_tempat_bekerja[0] if self.pengajuan_tempo_id.dokumen_tempat_bekerja else []
+ if self.user_company_id.dokumen_tempat_bekerja:
+ self.user_company_id.message_post(body='Dokumen Tempat Bekerja',
+ attachment_ids=[self.user_company_id.dokumen_tempat_bekerja.id])
+ # self.user_company_id.active = True
+ # user.send_company_request_approve_mail()
+ self.user_company_id.property_payment_term_id = self.pengajuan_tempo_id.tempo_duration.id
+ self.user_company_id.active_limit = True
+ self.user_company_id.warning_stage = float(limit_tempo) - (float(limit_tempo)/2)
+ self.user_company_id.blocking_stage = limit_tempo
+
+ # Internal Notes
+ comment = []
+ if self.pengajuan_tempo_id.tukar_invoice:
+ comment.append(f"Jadwal Tukar Invoice: {self.pengajuan_tempo_id.tukar_invoice}")
+ if self.pengajuan_tempo_id.jadwal_bayar:
+ comment.append(f"Jadwal Pembayaran: {self.pengajuan_tempo_id.jadwal_bayar}")
+ self.user_company_id.comment = "\n".join(comment)
+
+
+ # template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_approved')
+ # tempo = self.pengajuan_tempo_id
+ # template.send_mail(tempo.id, force_send=True)
+ template = self.env.ref('indoteknik_custom.mail_template_res_user_company_tempo_approved')
+ template.send_mail(self.id, force_send=True)
+ # self.user_id.parent_id = new_company.id
+ # user.send_company_request_reject_mail()
+ return super(UserPengajuanTempoRequest, self).write(vals)
+
+ def get_user_by_email(self, email):
+ return request.env['res.users'].search([
+ ('login', '=', email),
+ ('active', 'in', [True, False])
+ ])
+
+ def format_currency(self, number):
+ number = int(number)
+ return "{:,}".format(number).replace(',', '.') \ No newline at end of file
diff --git a/indoteknik_custom/models/wati.py b/indoteknik_custom/models/wati.py
index f3632334..a0619f83 100644
--- a/indoteknik_custom/models/wati.py
+++ b/indoteknik_custom/models/wati.py
@@ -32,28 +32,43 @@ class WatiNotification(models.Model):
]).unlink()
_logger.info('Success Cleanup WATI Notification')
- def _parse_notification(self, limit = 0):
+ def _parse_notification(self, limit=0):
domain = [('is_parsed', '=', False)]
notifications = self.search(domain, order='id', limit=limit)
notification_not_parsed_count = self.search_count(domain)
i = 0
for notification in notifications:
i += 1
- _logger.info('[Parse Notification][%s] Process: %s/%s | Not Parsed: %s' % (notification.id, i, str(limit), str(notification_not_parsed_count)))
+ _logger.info('[Parse Notification][%s] Process: %s/%s | Not Parsed: %s' %
+ (notification.id, i, str(limit), str(notification_not_parsed_count)))
+
notification_json = json.loads(notification.json_raw)
sender_name = 'Indoteknik'
if 'senderName' in notification_json:
sender_name = notification_json['senderName']
- ticket_id = notification_json['ticketId']
- date_wati = float(notification_json['timestamp'])
- date_wati = datetime.fromtimestamp(date_wati)
+ ticket_id = notification_json.get('ticketId')
+ timestamp = notification_json.get('timestamp')
+
+ if not timestamp:
+ _logger.warning('[Parse Notification][%s] Missing timestamp in notification JSON: %s' %
+ (notification.id, notification.json_raw))
+ continue # Skip this notification
+
+ try:
+ date_wati = datetime.fromtimestamp(float(timestamp))
+ except ValueError as e:
+ _logger.error('[Parse Notification][%s] Invalid timestamp format: %s. Error: %s' %
+ (notification.id, timestamp, str(e)))
+ continue
+
wati_history = self.env['wati.history'].search([('ticket_id', '=', ticket_id)], limit=1)
if wati_history:
self._create_wati_history_line(wati_history, ticket_id, sender_name, notification_json, date_wati)
else:
new_header = self._create_wati_history_header(ticket_id, sender_name, notification_json, date_wati)
self._create_wati_history_line(new_header, ticket_id, sender_name, notification_json, date_wati)
+
notification.is_parsed = True
return
@@ -217,26 +232,42 @@ class WatiHistory(models.Model):
limit = 50
wati_histories = self.env['wati.history'].search(domain, limit=limit)
count = 0
+
for wati_history in wati_histories:
count += 1
- _logger.info('[Parse Notification] Process: %s/%s' % (str(count), str(limit)))
+ _logger.info('[Parse Notification] Processing: %s/%s', count, limit)
wati_api = self.env['wati.api']
- # Perbaikan pada params 'attribute' untuk menghindari masalah "type object is not subscriptable"
+ # Perbaikan pada parameter JSON
params = {
'pageSize': 1,
'pageNumber': 1,
- 'attribute': json.dumps([{'name': "phone", 'operator': "contain", 'value': wati_history.wa_id}]),
+ 'attribute': json.dumps([
+ {'name': "phone", 'operator': "contain", 'value': wati_history.wa_id}
+ ]),
}
- wati_contacts = wati_api.http_get('/api/v1/getContacts', params)
+ try:
+ wati_contacts = wati_api.http_get('/api/v1/getContacts', params)
+ except Exception as e:
+ _logger.error('Error while calling WATI API: %s', str(e))
+ continue
+ # Validasi respons dari API
+ if not isinstance(wati_contacts, dict):
+ _logger.error('Invalid response format from WATI API: %s', wati_contacts)
+ continue
+
if wati_contacts.get('result') != 'success':
- return
+ _logger.warning('WATI API request failed with result: %s', wati_contacts.get('result'))
+ continue
contact_list = wati_contacts.get('contact_list', [])
-
+ if not contact_list:
+ _logger.info('No contacts found for WA ID: %s', wati_history.wa_id)
+ continue
+
perusahaan = email = ''
for data in contact_list:
custom_params = data.get('customParams', [])
@@ -247,12 +278,14 @@ class WatiHistory(models.Model):
perusahaan = value
elif name == 'email':
email = value
- # End inner loop
# Update wati_history fields
- wati_history.perusahaan = perusahaan
- wati_history.email = email
- wati_history.is_get_attribute = True
+ wati_history.write({
+ 'perusahaan': perusahaan,
+ 'email': email,
+ 'is_get_attribute': True,
+ })
+ _logger.info('Wati history updated: %s', wati_history.id)
# @api.onchange('last_reply_date')
# def _compute_expired_date(self):
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index c2ff2a3b..4e1ecd7d 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -44,6 +44,7 @@ access_wati_notification,access.wati.notification,model_wati_notification,,1,1,1
access_wati_api,access.wati.api,model_wati_api,,1,1,1,1
access_wati_contact,access.wati.contact,model_wati_contact,,1,1,1,1
access_user_company_request,access.user.company.request,model_user_company_request,,1,1,1,1
+access_user_pengajuan_tempo_request,access.user.pengajuan.tempo.request,model_user_pengajuan_tempo_request,,1,1,1,1
access_res_partner_company_type,access.res.partner.company_type,model_res_partner_company_type,,1,1,1,1
access_uangmuka_penjualan,access.uangmuka.penjualan,model_uangmuka_penjualan,,1,1,1,1
access_uangmuka_pembelian,access.uangmuka.pembelian,model_uangmuka_pembelian,,1,1,1,1
@@ -145,5 +146,14 @@ access_web_find_page,access.web.find.page,model_web_find_page,,1,1,1,1
access_v_requisition_match_po,access.v.requisition.match.po,model_v_requisition_match_po,,1,1,1,1
access_approval_retur_picking,access.approval.retur.picking,model_approval_retur_picking,,1,1,1,1
access_sales_order_fulfillment_v2,access.sales.order.fulfillment.v2,model_sales_order_fulfillment_v2,,1,1,1,1
+access_v_move_outstanding,access.v.move.outstanding,model_v_move_outstanding,,1,1,1,1
access_va_multi_approve,access.va.multi.approve,model_va_multi_approve,,1,1,1,1
access_va_multi_reject,access.va.multi.reject,model_va_multi_reject,,1,1,1,1
+access_stock_immediate_transfer,access.stock.immediate.transfer,model_stock_immediate_transfer,,1,1,1,1
+access_coretax_faktur,access.coretax.faktur,model_coretax_faktur,,1,1,1,1
+access_purchase_order_unlock_wizard,access.purchase.order.unlock.wizard,model_purchase_order_unlock_wizard,,1,1,1,1
+
+access_User_pengajuan_tempo_line,access.user.pengajuan.tempo.line,model_user_pengajuan_tempo_line,,1,1,1,1
+access_user_pengajuan_tempo,access.user.pengajuan.tempo,model_user_pengajuan_tempo,,1,1,1,1
+access_reject_reason_wizard,reject.reason.wizard,model_reject_reason_wizard,,1,1,1,0
+access_confirm_approval_wizard,confirm.approval.wizard,model_confirm_approval_wizard,,1,1,1,0
diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml
index 2863af57..4cc35b6d 100644
--- a/indoteknik_custom/views/account_move.xml
+++ b/indoteknik_custom/views/account_move.xml
@@ -163,5 +163,13 @@
<field name="state">code</field>
<field name="code">action = records.open_form_multi_create_reklas_penjualan()</field>
</record>
+
+ <record id="action_export_faktur" model="ir.actions.server">
+ <field name="name">Export Faktur ke XML</field>
+ <field name="model_id" ref="account.model_account_move" />
+ <field name="binding_model_id" ref="account.model_account_move" />
+ <field name="state">code</field>
+ <field name="code">action = records.export_faktur_to_xml()</field>
+ </record>
</data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/coretax_faktur.xml b/indoteknik_custom/views/coretax_faktur.xml
new file mode 100644
index 00000000..45eea23f
--- /dev/null
+++ b/indoteknik_custom/views/coretax_faktur.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <record id="act_coretax_faktur" model="ir.actions.act_window">
+ <field name="name">Export Faktur Pajak Keluaran XML Version</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">coretax.faktur</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ </record>
+
+ <record id="view_coretax_faktur" model="ir.ui.view">
+ <field name="name">view coretax faktur</field>
+ <field name="model">coretax.faktur</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Export Pajak Keluaran">
+ <p>
+ Klik tombol Export di bawah untuk mulai export Faktur Pajak Keluaran.
+ Data yang diexport adalah Customer Invoice yang berstatus Open dan belum diexport ke E-Faktur.
+ </p>
+
+ <p>
+ Setelah proses export Faktur Pajak Keluaran selesai dilakukan,
+ download file ini:
+ </p>
+ <group>
+ <field name="export_file" filename="export_filename" readonly="1"/>
+ </group>
+
+ <p>
+ Lalu import ke program E-Faktur DJP melalui menu <b>Referensi - Pajak Keluaran - Import</b>
+ </p>
+
+ <footer>
+ <button string="Export" name="export_to_download" type="object" class="btn-primary"/>
+ <button string="Cancel" class="btn-default" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <menuitem id="coretax_faktur_export"
+ parent="vit_efaktur.menu_vit_efaktur_keluaran"
+ sequence="80"
+ name="Export FP. Keluaran XML"
+ action="act_coretax_faktur" />
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/product_template.xml b/indoteknik_custom/views/product_template.xml
index 5a509ebd..af21984a 100755
--- a/indoteknik_custom/views/product_template.xml
+++ b/indoteknik_custom/views/product_template.xml
@@ -20,10 +20,21 @@
<field name="unpublished" />
<field name="desc_update_solr" readonly="1" />
<field name="last_update_solr" readonly="1" />
+ <!-- <field name="qr_code" widget="image" invisible="1"/> -->
</field>
<field name="public_categ_ids" position="attributes">
<attribute name="required">0</attribute>
</field>
+ <div name="options" position="inside">
+ <div>
+ <field name="merchandise_ok"/>
+ <label for="merchandise_ok"/>
+ </div>
+ <div>
+ <field name="print_barcode"/>
+ <label for="print_barcode"/>
+ </div>
+ </div>
<field name="public_categ_ids" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
</field>
@@ -52,6 +63,7 @@
<field name="arch" type="xml">
<field name="last_update_solr" position="after">
<field name="clean_website_description" />
+ <field name="qr_code_variant" widget="image" readonly="True"/>
</field>
</field>
</record>
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index 0e6b6792..a70b2058 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -20,6 +20,11 @@
type="object"
attrs="{'invisible': [('approval_status', '=', 'approved')]}"
/>
+ <button name="po_approve_unlock"
+ string="Ask Approval Unlock PO"
+ type="object"
+ attrs="{'invisible': [('approval_status_unlock', '=', 'approved')]}"
+ />
<button name="indoteknik_custom.action_view_uangmuka_pembelian" string="UangMuka"
type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}"/>
</button>
@@ -41,6 +46,10 @@
</field>
<field name="approval_status" position="after">
<field name="revisi_po"/>
+ <field name="not_update_purchasepricelist"/>
+ </field>
+ <field name="approval_status" position="after">
+ <field name="approval_status_unlock" invisible="True"/>
</field>
<field name="incoterm_id" position="after">
<field name="amount_total_without_service"/>
@@ -51,6 +60,8 @@
<field name="summary_qty_po"/>
<field name="count_line_product"/>
<field name="payment_term_id"/>
+ <field name="total_cost_service" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
+ <field name="total_delivery_amt" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
</field>
<field name="amount_total" position="after">
<field name="total_margin"/>
@@ -58,6 +69,9 @@
<field name="total_percent_margin"/>
<field name="total_so_percent_margin"/>
</field>
+ <field name="partner_ref" position="before">
+ <field name="store_name" attrs="{'invisible': [('partner_id', 'not in', [9688, 29712])]}"/>
+ </field>
<field name="product_id" position="before">
<field name="line_no" attrs="{'readonly': 1}" optional="hide"/>
</field>
@@ -73,6 +87,13 @@
<field name="qty_available" readonly="1" optional="hide"/>
<field name="qty_reserved" readonly="1" optional="hide"/>
<field name="suggest" readonly="1" widget="badge" decoration-danger="suggest == 'harus beli'" decoration-success="suggest == 'masih cukup'"/>
+ <field name="delivery_amt" optional="hide"/>
+ <field name="delivery_amt_per_item" optional="hide"/>
+ <field name="contribution_delivery_amt" optional="hide"/>
+ <field name="cost_service" optional="hide"/>
+ <field name="cost_service_per_item" optional="hide"/>
+ <field name="contribution_cost_service" optional="hide"/>
+ <field name="ending_price" optional="hide"/>
<!-- <field name="suggest" readonly="1"/> -->
</field>
<field name="product_id" position="before">
@@ -136,7 +157,30 @@
</field>
</record>
</data>
-
+ <data>
+ <record id="view_purchase_order_unlock_wizard_form" model="ir.ui.view">
+ <field name="name">purchase.order.unlock.wizard.form</field>
+ <field name="model">purchase.order.unlock.wizard</field>
+ <field name="arch" type="xml">
+ <form string="Reject Reason">
+ <group>
+ <field name="alasan" widget="text"/>
+ </group>
+ <footer>
+ <button string="Confirm" type="object" name="confirm_reject" class="btn-primary"/>
+ <button string="Cancel" class="btn-secondary" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_purchase_order_unlock_wizard" model="ir.actions.act_window">
+ <field name="name">Reject Reason</field>
+ <field name="res_model">purchase.order.unlock.wizard</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ </record>
+ </data>
<data>
<record id="rfq_order_tree_view_inherit" model="ir.ui.view">
@@ -152,6 +196,9 @@
<field name="logbook_bill_id" optional="hide"/>
<field name="status_printed" optional="hide"/>
</field>
+ <field name="partner_id" position="after">
+ <field name="store_name"/>
+ </field>
</field>
</record>
</data>
diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml
index 1b164161..bd664890 100644
--- a/indoteknik_custom/views/res_partner.xml
+++ b/indoteknik_custom/views/res_partner.xml
@@ -56,6 +56,9 @@
<field name="npwp" position="before">
<field name="customer_type" required="1"/>
</field>
+ <field name="alamat_lengkap_text" position="after">
+ <field name="nitku" />
+ </field>
<field name="is_berikat" position="after">
<field name="pakta_integritas"/>
</field>
@@ -75,11 +78,114 @@
<field name="site_id" attrs="{'readonly': [('parent_id', '=', False)]}" domain="[('partner_id', '=', main_parent_id)]" context="{'default_partner_id': active_id}" />
</xpath>
<xpath expr="//field[@name='property_payment_term_id']" position="attributes">
- <attribute name="readonly">1</attribute>
+ <attribute name="readonly">0</attribute>
</xpath>
<xpath expr="//field[@name='property_supplier_payment_term_id']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
+ <notebook>
+ <page string="Pengajuan Tempo">
+ <!-- Informasi Usaha Section -->
+ <group string="Informasi Usaha" colspan="4">
+ <group>
+ <field name="name_tempo"/>
+ <field name="industry_id_tempo"/>
+ <field name="street_tempo"/>
+ <field name="state_id_tempo"/>
+ <field name="city_id_tempo"/>
+ <field name="zip_tempo"/>
+ <field name="mobile_tempo"/>
+ <field name="bank_name_tempo"/>
+ <field name="account_name_tempo"/>
+ <field name="account_number_tempo"/>
+ <field name="website_tempo"/>
+ <field name="portal"/>
+ <field name="estimasi_tempo"/>
+ <field name="tempo_duration"/>
+ <field name="tempo_limit"/>
+ <field name="category_produk_ids" widget="many2many_tags"/>
+ </group>
+ </group>
+
+ <!-- Kontak Perusahaan Section -->
+ <group string="Kontak Perusahaan" colspan="4">
+ <group>
+ <field name="direktur_name"/>
+ <field name="direktur_mobile"/>
+ <field name="direktur_email"/>
+ </group>
+ <group>
+ <field name="purchasing_name"/>
+ <field name="purchasing_mobile"/>
+ <field name="purchasing_email"/>
+ </group>
+ <group>
+ <field name="finance_name"/>
+ <field name="finance_mobile"/>
+ <field name="finance_email"/>
+ </group>
+ </group>
+
+ <!-- Pengiriman Section -->
+ <group string="Pengiriman" colspan="4">
+ <group>
+ <field name="pic_name"/>
+ <field name="street_pengiriman"/>
+ <field name="state_id_pengiriman"/>
+ <field name="city_id_pengiriman"/>
+ <field name="district_id_pengiriman"/>
+ <field name="subDistrict_id_pengiriman"/>
+ <field name="zip_pengiriman"/>
+ </group>
+ <group>
+ <field name="invoice_pic"/>
+ <field name="street_invoice"/>
+ <field name="state_id_invoice"/>
+ <field name="city_id_invoice"/>
+ <field name="district_id_invoice"/>
+ <field name="subDistrict_id_invoice"/>
+ <field name="zip_invoice"/>
+ </group>
+ <group>
+ <field name="tukar_invoice"/>
+ <field name="jadwal_bayar"/>
+ <field name="dokumen_pengiriman"/>
+ <field name="dokumen_pengiriman_input"/>
+ <field name="dokumen_invoice"/>
+ </group>
+ </group>
+
+ <!-- Supplier Lines Section -->
+ <group string="Suppliers">
+ <field name="supplier_ids">
+ <tree>
+ <field name="name_supplier"/>
+ <field name="pic_name"/>
+ <field name="phone"/>
+ <field name="tempo_duration"/>
+ <field name="credit_limit"/>
+ </tree>
+ </field>
+ </group>
+
+ <group string="Dokumen">
+ <field name="dokumen_npwp" />
+ <field name="dokumen_sppkp" />
+ <field name="dokumen_nib" />
+ <field name="dokumen_siup" />
+ <field name="dokumen_tdp" />
+ <field name="dokumen_skdp" />
+ <field name="dokumen_skt"/>
+ <field name="dokumen_akta_pendirian" />
+ <field name="dokumen_akta_perubahan" />
+ <field name="dokumen_tempat_bekerja" />
+ <field name="dokumen_foto_kantor" />
+ <field name="dokumen_ktp_dirut" />
+ <field name="dokumen_laporan_keuangan" />
+ <field name="dokumen_ktp_dirut" />
+ </group>
+ </page>
+ </notebook>
</field>
</record>
</data>
diff --git a/indoteknik_custom/views/res_users.xml b/indoteknik_custom/views/res_users.xml
index 911225c0..9553bb91 100644
--- a/indoteknik_custom/views/res_users.xml
+++ b/indoteknik_custom/views/res_users.xml
@@ -402,5 +402,7 @@
</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 e12130de..008a04ed 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -14,7 +14,7 @@
<button name="sale_order_approve"
string="Ask Approval"
type="object"
- attrs="{'invisible': [('approval_status', 'in', ['pengajuan1', 'pengajuan2', 'approved'])]}"
+ attrs="{'invisible': [('approval_status', '=', ['approved'])]}"
/>
<button name="action_web_approve"
string="Web Approve"
@@ -107,6 +107,14 @@
{'readonly': [('state', 'in', ('done','cancel'))]}
</attribute>
</xpath>
+ <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree" position="inside">
+ <field name="desc_updatable" invisible="1"/>
+ </xpath>
+ <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='name']" position="attributes">
+ <attribute name="attrs">
+ {'readonly': [('desc_updatable', '=', False)]}
+ </attribute>
+ </xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_unit']" position="attributes">
<attribute name="attrs">
{
@@ -263,9 +271,9 @@
<page string="Matches PO" name="page_matches_po" invisible="1">
<field name="order_sales_match_line" readonly="1"/>
</page>
- <page string="Fullfillment" name="page_sale_order_fullfillment">
+ <!-- <page string="Fullfillment" name="page_sale_order_fullfillment">
<field name="fullfillment_line" readonly="1"/>
- </page>
+ </page> -->
<page string="Fulfillment v2" name="page_sale_order_fullfillment2">
<field name="fulfillment_line_v2" readonly="1"/>
</page>
diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index 1893fcaf..8acba608 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -56,10 +56,22 @@
string="Biteship"
type="object"
/>
+ <button name="track_envio_shipment"
+ string="Tracking Envio"
+ type="object"
+ attrs="{'invisible': [('carrier_id', '!=', 151)]}"
+ />
+ <button name="action_fetch_lalamove_order"
+ string="Tracking Lalamove"
+ type="object"
+ attrs="{'invisible': [('carrier_id', '!=', 9)]}"
+ />
</button>
<field name="backorder_id" position="after">
<field name="summary_qty_detail"/>
<field name="count_line_detail"/>
+ <field name="dokumen_tanda_terima"/>
+ <field name="dokumen_pengiriman"/>
</field>
<field name="weight_uom_name" position="after">
<group>
@@ -74,6 +86,9 @@
<field name="partner_id" position="after">
<field name="real_shipping_id"/>
</field>
+ <field name="product_uom_qty" position="attributes">
+ <attribute name="attrs">{'readonly': [('parent.picking_type_code', '=', 'incoming')]}</attribute>
+ </field>
<field name="date_done" position="after">
<field name="arrival_time"/>
</field>
@@ -115,6 +130,8 @@
</field>
<field name="product_uom" position="after">
<field name="sale_id" attrs="{'readonly': 1}" optional="hide"/>
+ <field name="print_barcode" optional="hide"/>
+ <field name="qr_code_variant" widget="image" optional="hide"/>
</field>
<page name="note" position="after">
<page string="E-Faktur" name="efaktur" attrs="{'invisible': [['is_internal_use', '=', False]]}">
@@ -143,13 +160,57 @@
<field name="sj_documentation" widget="image" />
<field name="paket_documentation" widget="image" />
</group>
+ <group attrs="{'invisible': [('carrier_id', '!=', 151)]}">
+ <field name="envio_id" invisible="1"/>
+ <field name="envio_code"/>
+ <field name="envio_ref_code"/>
+ <field name="envio_eta_at"/>
+ <field name="envio_ata_at"/>
+ <field name="envio_etd_at" invisible="1"/>
+ <field name="envio_atd_at" invisible="1"/>
+ <field name="envio_received_by"/>
+ <field name="envio_status"/>
+ <field name="envio_cod_value" invisible="1"/>
+ <field name="envio_cod_status" invisible="1"/>
+ <field name="envio_latest_message"/>
+ <field name="envio_latest_recorded_at"/>
+ <field name="envio_latest_latitude" invisible="1"/>
+ <field name="envio_latest_longitude" invisible="1"/>
+ <field name="tracking_by" invisible="1"/>
+ </group>
+ <group attrs="{'invisible': [('carrier_id', '!=', 9)]}">
+ <field name="lalamove_data" invisible="1"/>
+ <field name="lalamove_order_id"/>
+ <field name="lalamove_address"/>
+ <field name="lalamove_name"/>
+ <field name="lalamove_phone"/>
+ <field name="lalamove_status"/>
+ <field name="lalamove_delivered_at"/>
+ <field name="lalamove_image_url" invisible="1"/>
+ <field name="lalamove_image_html"/>
+ </group>
</group>
</page>
+ <!-- <page string="Check Product" name="check_product">
+ <field name="check_product_lines"/>
+ </page> -->
</page>
</field>
</record>
+ <!-- <record id="check_product_tree" model="ir.ui.view">
+ <field name="name">check.product.tree</field>
+ <field name="model">check.product</field>
+ <field name="arch" type="xml">
+ <tree editable="bottom" decoration-warning="status == 'Pending'" decoration-success="status == 'Done'">
+ <field name="product_id"/>
+ <field name="quantity"/>
+ <field name="status"/>
+ </tree>
+ </field>
+ </record> -->
+
<record id="view_stock_move_line_detailed_operation_tree_inherit" model="ir.ui.view">
<field name="name">stock.move.line.operations.tree.inherit</field>
<field name="model">stock.move.line</field>
diff --git a/indoteknik_custom/views/user_company_request.xml b/indoteknik_custom/views/user_company_request.xml
index 0600aa8e..88d04c64 100644
--- a/indoteknik_custom/views/user_company_request.xml
+++ b/indoteknik_custom/views/user_company_request.xml
@@ -54,12 +54,16 @@
<field name="res_model">user.company.request</field>
<field name="view_mode">tree,form</field>
</record>
+ <menuitem id="res_partner_menu_user"
+ name="User Request"
+ parent="contacts.menu_contacts"
+ groups="base.group_system"
+ sequence="3"/>
- <menuitem
- id="menu_user_company_request"
- name="User Company Request"
- parent="contacts.menu_contacts"
- sequence="3"
- action="action_user_company_request"
- />
+<menuitem id="menu_user_company_request"
+ name="User Company Request"
+ parent="res_partner_menu_user"
+ sequence="4"
+ action="action_user_company_request"
+ groups="base.group_system"/>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/user_pengajuan_tempo.xml b/indoteknik_custom/views/user_pengajuan_tempo.xml
new file mode 100644
index 00000000..33ad91cf
--- /dev/null
+++ b/indoteknik_custom/views/user_pengajuan_tempo.xml
@@ -0,0 +1,344 @@
+<odoo>
+ <!-- Form view for userPengajuanTempo -->
+ <record id="view_user_pengajuan_tempo_form" model="ir.ui.view">
+ <field name="name">user.pengajuan.tempo.form</field>
+ <field name="model">user.pengajuan.tempo</field>
+ <field name="arch" type="xml">
+ <form string="Pengajuan Tempo">
+ <sheet>
+ <!-- Informasi Usaha Section -->
+ <group string="Informasi Usaha" colspan="4">
+ <group>
+ <field name="name_tempo"/>
+ <field name="industry_id_tempo"/>
+ <field name="street_tempo"/>
+ <field name="state_id_tempo"/>
+ <field name="city_id_tempo"/>
+ <field name="district_id_tempo"/>
+ <field name="subDistrict_id_tempo"/>
+ <field name="zip_tempo"/>
+ <field name="mobile_tempo"/>
+ <field name="bank_name_tempo"/>
+ <field name="account_name_tempo"/>
+ <field name="account_number_tempo"/>
+ <field name="website_tempo"/>
+ <field name="portal"/>
+ <field name="estimasi_tempo"/>
+ <field name="tempo_duration"/>
+ <field name="tempo_limit"/>
+ <field name="category_produk_ids" widget="many2many_tags"/>
+ </group>
+ </group>
+
+ <!-- Kontak Perusahaan Section -->
+ <group string="Kontak Perusahaan" colspan="4">
+ <group>
+ <field name="direktur_name"/>
+ <field name="direktur_mobile"/>
+ <field name="direktur_email"/>
+ </group>
+ <group>
+ <field name="purchasing_name"/>
+ <field name="purchasing_mobile"/>
+ <field name="purchasing_email"/>
+ </group>
+ <group>
+ <field name="finance_name"/>
+ <field name="finance_mobile"/>
+ <field name="finance_email"/>
+ </group>
+ </group>
+
+ <!-- Pengiriman Section -->
+ <group string="Pengiriman" colspan="4">
+ <group>
+ <field name="pic_name"/>
+ <field name="street_pengiriman"/>
+ <field name="state_id_pengiriman"/>
+ <field name="city_id_pengiriman"/>
+ <field name="district_id_pengiriman"/>
+ <field name="subDistrict_id_pengiriman"/>
+ <field name="zip_pengiriman"/>
+ </group>
+ <group>
+ <field name="invoice_pic"/>
+ <field name="street_invoice"/>
+ <field name="state_id_invoice"/>
+ <field name="city_id_invoice"/>
+ <field name="district_id_invoice"/>
+ <field name="subDistrict_id_invoice"/>
+ <field name="zip_invoice"/>
+ </group>
+ <group>
+ <field name="tukar_invoice"/>
+ <field name="jadwal_bayar"/>
+ <field name="dokumen_pengiriman"/>
+ <field name="dokumen_pengiriman_input"/>
+ <field name="dokumen_invoice"/>
+ </group>
+ </group>
+
+ <!-- Supplier Lines Section -->
+ <group string="Suppliers">
+ <field name="supplier_ids">
+ <tree>
+ <field name="name_supplier"/>
+ <field name="pic_name"/>
+ <field name="phone"/>
+ <field name="tempo_duration"/>
+ <field name="credit_limit"/>
+ </tree>
+ </field>
+ </group>
+
+ <group string="Dokumen">
+ <field name="dokumen_npwp" widget="many2many_binary" />
+ <field name="dokumen_sppkp" widget="many2many_binary" />
+ <field name="dokumen_nib" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_siup" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_tdp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_skdp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_skt" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_akta_pendirian" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_akta_perubahan" widget="many2many_binary" />
+ <field name="dokumen_tempat_bekerja" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_foto_kantor" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_ktp_dirut" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_laporan_keuangan" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_ktp_dirut" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ </group>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <!-- Tree view for userPengajuanTempo -->
+ <record id="view_user_pengajuan_tempo_tree" model="ir.ui.view">
+ <field name="name">user.pengajuan.tempo.tree</field>
+ <field name="model">user.pengajuan.tempo</field>
+ <field name="arch" type="xml">
+ <tree string="Pengajuan Tempo">
+ <field name="name_tempo"/>
+ <field name="industry_id_tempo"/>
+ <field name="tempo_limit"/>
+ <field name="estimasi_tempo"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="mail_template_res_user_company_request_tempo_review" model="mail.template">
+ <field name="name">Users: Company Request Tempo Review</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo"/>
+ <field name="subject">Permohonan Tempo Anda Sedang Ditinjau</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">${object.user_id.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>Hai. ${object.name_tempo.name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas kepercayaan Anda memilih indoteknik.com. Kami telah menerima permohonan pembayaran tempo Anda.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Saat ini, tim kami sedang melakukan peninjauan terhadap permohonan Anda. Proses peninjauan ini membutuhkan waktu.</td></tr>
+ <tr>
+ <td style="padding-bottom: 16px; ">Kami akan informasikan kembali mengenai hasil peninjauan pembayaran tempo anda melalui email ini.</td>
+ </tr>
+
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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_new_tempo_to_sales" model="mail.template">
+ <field name="name">Users: Company New Tempo To Sales</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo"/>
+ <field name="subject">Pengajuan Tempo Harus di Periksa!</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">vita@indoteknik.co.id</field>
+<!-- <field name="email_to">sapiabon768@gmail.com</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 Manager Tim Sales, Novita Mayasari.</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Kami sampaikan informasi bahwa saat ini terdapat pengajuan Pembayaran Tempo dari ${object.name_tempo.name} melalui website Indoteknik.com.</td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Mohon dapat segera melakukan pengecekan dan evaluasi terhadap data perusahaan yang telah dilampirkan tersebut agar proses pengajuan dapat diproses lebih lanjut.</td></tr>
+
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatian dan kerjasamanya.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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_tempo_approved_by_sales" model="mail.template">
+ <field name="name">Users: Company Tempo Approved By Sales</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo_request"/>
+ <field name="subject">Pengajuan Tempo Harus di Periksa!</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">widyariyanti97@gmail.com, stephan@indoteknik.co.id</field>
+<!-- <field name="email_to">sapiabon768@gmail.com</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 Tim Finance Indoteknik,</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Kami informasikan bahwa pengajuan tempo dari ${object.pengajuan_tempo_id.name_tempo.name} telah disetujui oleh Manager Divisi Sales.</td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Untuk kelancaran proses selanjutnya, kami mohon kesediaannya untuk melakukan pengecekan ulang terhadap pengajuan tempo tersebut.</td></tr>
+
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatian dan kerjasamanya.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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>
+
+ <!-- Action to open form and tree views -->
+ <record id="action_user_pengajuan_tempo" model="ir.actions.act_window">
+ <field name="name">Pengajuan Tempo</field>
+ <field name="res_model">user.pengajuan.tempo</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <!-- Menu item to access Pengajuan Tempo -->
+<!-- <menuitem id="menu_user_pengajuan_tempo_root" name="Pengajuan Tempo" />-->
+<!-- <menuitem id="menu_user_pengajuan_tempo" name="Pengajuan Tempo Records" parent="menu_user_pengajuan_tempo_root" action="action_user_pengajuan_tempo"/>-->
+</odoo>
diff --git a/indoteknik_custom/views/user_pengajuan_tempo_line.xml b/indoteknik_custom/views/user_pengajuan_tempo_line.xml
new file mode 100644
index 00000000..60b510bc
--- /dev/null
+++ b/indoteknik_custom/views/user_pengajuan_tempo_line.xml
@@ -0,0 +1,42 @@
+<odoo>
+ <record id="view_user_pengajuan_tempo_line_tree" model="ir.ui.view">
+ <field name="name">user_pengajuan.tempo.line.tree</field>
+ <field name="model">user_pengajuan.tempo.line</field>
+ <field name="arch" type="xml">
+ <tree string="Pengajuan Tempo Lines">
+ <field name="name_supplier" string="Nama Supplier"/>
+ <field name="pic_name" string="PIC"/>
+ <field name="phone" string="Telepon"/>
+ <field name="tempo_duration" string="Durasi Tempo"/>
+ <field name="credit_limit" string="Credit Limit"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_user_pengajuan_tempo_line_form" model="ir.ui.view">
+ <field name="name">pengajuan.tempo.line.form</field>
+ <field name="model">pengajuan.tempo.line</field>
+ <field name="arch" type="xml">
+ <form string="Pengajuan Tempo Line">
+ <group>
+ <field name="name_supplier"/>
+ <field name="pic_name"/>
+ <field name="phone"/>
+ <field name="tempo_duration"/>
+ <field name="credit_limit"/>
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <!-- Action to open the list view -->
+ <record id="action_user_pengajuan_tempo_line" model="ir.actions.act_window">
+ <field name="name">Pengajuan Tempo Lines</field>
+ <field name="res_model">pengajuan.tempo.line</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <!-- Add a menu item to access the list view -->
+ <menuitem id="menu_user_pengajuan_tempo_line_root" name="Pengajuan Tempo" />
+ <menuitem id="menu_user_pengajuan_tempo_line" name="Pengajuan Tempo Lines" parent="menu_user_pengajuan_tempo_line_root" action="action_user_pengajuan_tempo_line"/>
+</odoo>
diff --git a/indoteknik_custom/views/user_pengajuan_tempo_request.xml b/indoteknik_custom/views/user_pengajuan_tempo_request.xml
new file mode 100644
index 00000000..bb8262c9
--- /dev/null
+++ b/indoteknik_custom/views/user_pengajuan_tempo_request.xml
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <record id="user_pengajuan_tempo_request_tree" model="ir.ui.view">
+ <field name="name">user.pengajuan.tempo.request.tree</field>
+ <field name="model">user.pengajuan.tempo.request</field>
+ <field name="arch" type="xml">
+ <tree create="1" default_order="create_date desc">
+ <field name="user_id"/>
+ <field name="pengajuan_tempo_id"/>
+ <field name="create_date"/>
+ <field name="tempo_duration" attrs="{'readonly': [('state_tempo', '=', 'approval_director')]}"/>
+ <field name="tempo_limit" attrs="{'readonly': [('state_tempo', '=', 'approval_director')]}" placeholder="Contoh format, misalnya '10000000'" widget="monetary" options="{'currency_field': 'currency_id'}"/>
+ <field name="state_tempo" decoration-success="state_tempo == 'approval_director'" decoration-danger="state_tempo == 'reject'" widget="badge" optional="show"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="user_pengajuan_tempo_request_form" model="ir.ui.view">
+ <field name="name">user.pengajuan.tempo.request.form</field>
+ <field name="model">user.pengajuan.tempo.request</field>
+ <field name="arch" type="xml">
+ <form create="0">
+ <header>
+ <button name="button_approve"
+ string="Approve Tempo"
+ attrs="{'invisible': [('state_tempo', 'in', ['approval_director','reject'])]}"
+ type="object"
+ class="oe_highlight"/>
+ <button name="button_reject"
+ string="Reject"
+ attrs="{'invisible': [('state_tempo', 'in', ['approval_director','reject'])]}"
+ type="object"
+ groups="purchase.group_purchase_manager"
+ class="oe_highlight"/>
+ <field name="state_tempo" widget="statusbar"
+ statusbar_visible="draft,approval_sales,approval_finance,approval_director"
+ statusbar_colors='{"reject":"red"}'/>
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="user_id" readonly="1"/>
+ <field name="pengajuan_tempo_id"/>
+ <field name="tempo_duration" attrs="{'readonly': [('state_tempo', '=', 'approval_director')]}"/>
+ <field name="tempo_limit"
+ attrs="{'readonly': [('state_tempo', '=', 'approval_director')]}"
+ placeholder="Contoh format, misalnya '10000000'" widget="monetary" options="{'currency_field': 'currency_id'}"/>
+ </group>
+ </group>
+ <notebook>
+ <page string="Informasi Usaha">
+ <!-- Informasi Usaha Section -->
+ <group>
+ <field name="name_tempo" />
+ <field name="industry_id_tempo" />
+ <field name="street_tempo" />
+ <field name="state_id_tempo" />
+ <field name="city_id_tempo" />
+ <field name="zip_tempo" />
+ <field name="mobile_tempo" />
+ <field name="bank_name_tempo" />
+ <field name="account_name_tempo" />
+ <field name="account_number_tempo" />
+ <field name="website_tempo" />
+ <field name="estimasi_tempo" />
+ <field name="tempo_duration_origin" />
+ <field name="tempo_limit_origin" />
+<!-- <field name="category_produk_ids" widget="many2many_tags" />-->
+ </group>
+ </page>
+ <page string="Kontak Perusahaan">
+ <group>
+ <field name="direktur_name"/>
+ <field name="direktur_mobile"/>
+ <field name="direktur_email"/>
+ </group>
+ <group>
+ <field name="purchasing_name"/>
+ <field name="purchasing_mobile"/>
+ <field name="purchasing_email"/>
+ </group>
+ <group>
+ <field name="finance_name"/>
+ <field name="finance_mobile"/>
+ <field name="finance_email"/>
+ </group>
+ </page>
+ <Page string="Pengiriman">
+ <group>
+ <field name="pic_name"/>
+ <field name="street_pengiriman"/>
+ <field name="state_id_pengiriman"/>
+ <field name="city_id_pengiriman"/>
+ <field name="district_id_pengiriman"/>
+ <field name="subDistrict_id_pengiriman"/>
+ <field name="zip_pengiriman"/>
+ </group>
+ <group>
+ <field name="invoice_pic"/>
+ <field name="street_invoice"/>
+ <field name="state_id_invoice"/>
+ <field name="city_id_invoice"/>
+ <field name="district_id_invoice"/>
+ <field name="subDistrict_id_invoice"/>
+ <field name="zip_invoice"/>
+ </group>
+ <group>
+ <field name="tukar_invoice"/>
+ <field name="jadwal_bayar"/>
+ <field name="dokumen_pengiriman"/>
+ <field name="dokumen_pengiriman_input"/>
+ <field name="dokumen_invoice"/>
+ </group>
+ </Page>
+ <Page string="Suppliers">
+ <field name="supplier_ids">
+ <tree>
+ <field name="name_supplier"/>
+ <field name="pic_name"/>
+ <field name="phone"/>
+ <field name="tempo_duration"/>
+ <field name="credit_limit"/>
+ </tree>
+ </field>
+ </Page>
+ <Page string="Dokumen">
+ <group >
+ <field name="dokumen_npwp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_sppkp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_nib" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_siup" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_tdp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_skdp" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_skt" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_akta_pendirian" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_akta_perubahan" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_tempat_bekerja" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_foto_kantor" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_ktp_dirut" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_laporan_keuangan" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ <field name="dokumen_ktp_dirut" widget="many2many_binary" options="{'no_create': True, 'no_open': False}"/>
+ </group>
+ </Page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+</record>
+
+
+
+
+ <!-- Wizard for Reject Reason -->
+<record id="view_reject_reason_wizard_form" model="ir.ui.view">
+ <field name="name">reject.reason.wizard.form</field>
+ <field name="model">reject.reason.wizard</field>
+ <field name="arch" type="xml">
+ <form string="Reject Reason">
+ <group>
+ <field name="reason_reject" widget="text"/>
+ </group>
+ <footer>
+ <button string="Confirm" type="object" name="confirm_reject" class="btn-primary"/>
+ <button string="Cancel" class="btn-secondary" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+</record>
+
+<record id="action_reject_reason_wizard" model="ir.actions.act_window">
+ <field name="name">Reject Reason</field>
+ <field name="res_model">reject.reason.wizard</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+</record>
+
+
+<record id="view_confirm_approval_wizard_form" model="ir.ui.view">
+ <field name="name">confirm.approval.wizard.form</field>
+ <field name="model">confirm.approval.wizard</field>
+ <field name="arch" type="xml">
+ <form string="Konfirmasi Approval">
+ <group>
+ <p>Apakah Anda yakin ingin mengapprove tempo ini?</p>
+ </group>
+ <footer>
+ <button string="Batal" class="btn-secondary" special="cancel"/>
+ <button string="Konfirmasi" type="object" name="confirm_approval" class="btn-primary"/>
+ </footer>
+ </form>
+ </field>
+</record>
+
+ <record id="mail_template_res_user_company_tempo_to_aprove_director" model="mail.template">
+ <field name="name">Users: Company Tempo To Approve Director</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo_request"/>
+ <field name="subject">Pengajuan Tempo Harus di Periksa!</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">akbar@fixcomart.co.id</field>
+<!-- <field name="email_to">sapiabon768@gmail.com</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 Pak Akbar,</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Kami sampaikan informasi bahwa saat ini terdapat pengajuan Pembayaran Tempo dari ${object.pengajuan_tempo_id.name_tempo.name} telah melalui tahap persetujuan awal dari Manager Sales &amp; diverifikasi oleh ${object.write_uid.name} dari tim Finance.</td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Untuk proses persetujuan akhir, mohon Bapak dapat segera meninjau dan mengevaluasi atas pengajuan terhadap data yang telah diberikan oleh ${object.pengajuan_tempo_id.name_tempo.name}.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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_tempo_approved" model="mail.template">
+ <field name="name">Users: Company Tempo Approved</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo_request"/>
+ <field name="subject">Pengajuan Tempo Anda di Indoteknik.com Telah Disetujui!</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">${object.user_id.email | 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>Hai. ${object.pengajuan_tempo_id.name_tempo.name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Kabar baik! Kami dengan senang hati menginformasikan bahwa pengajuan tempo pembayaran anda di Indoteknik.com telah disetujui dengan detail sebagai berikut.</td></tr>
+ <tr><td style="padding-bottom: 4px;"><b>Limit Pembayaran Tempo : Rp ${object.format_currency(object.user_id.tempo_limit)}</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;"><b>Durasi Pembayaran Tempo : ${object.user_id.tempo_duration.name}</b></td></tr>
+ <tr>
+ <td style="padding-bottom: 16px; ">Anda dapat melakukan pembayaran sesuai dengan jangka waktu yang telah disepakati. Detail lengkap mengenai transaksi anda, termasuk tanggal jatuh tempo &amp; Pengajuan Kenaikan Limit Tempo, dapat anda lihat di akun <a href="https://indoteknik.com/my/profile">Profile Indoteknik.com</a> .</td>
+ </tr>
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas kepercayaan anda kepada Indoteknik.com.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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_tempo_reject" model="mail.template">
+ <field name="name">Users: Company Tempo Reject</field>
+ <field name="model_id" ref="indoteknik_custom.model_user_pengajuan_tempo_request"/>
+ <field name="subject">Mohon Maaf Pengajuan Tempo anda kami Tolak</field>
+ <field name="email_from">"Indoteknik.com" &lt;noreply@indoteknik.com&gt;</field>
+ <field name="reply_to">sales@indoteknik.com</field>
+ <field name="email_to">${object.user_id.email | safe}</field>
+<!-- <field name="email_to">sapiabon768@gmail.com</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>Hai. ${object.pengajuan_tempo_id.name_tempo.name},</b></td></tr>
+
+ <tr><td style="padding-bottom: 16px;">Kami sampaikan dengan sangat menyesal bahwa pengajuan permohonan tempo pembayaran Anda yang diajukan pada tanggal ${format_date(object.pengajuan_tempo_id.create_date, 'dd MMMM yyyy')} tidak dapat kami setujui.</td></tr>
+ <tr><td style="padding-bottom: 16px;">Keputusan ini telah kami pertimbangkan secara matang berdasarkan beberapa faktor, diantaranya alasan tidak disetujuinya adalah ${object.reason_reject}</td></tr>
+ <tr>
+ <td style="padding-bottom: 16px; ">Kami memahami bahwa hal ini mungkin mengecewakan. Namun, kami berharap Anda dapat memahami kebijakan perusahaan kami.</td>
+ </tr><tr>
+ <td style="padding-bottom: 16px; ">Jangan khawatir anda dapat melakukan pembelian dengan metode pembayaran CBD (Cash Before Delivery) di website indoteknik.com.</td>
+ </tr>
+
+ <tr><td style="padding-bottom: 16px;">Terima kasih atas Perhatian &amp; Pengertiannya.</td></tr>
+
+ <tr><td style="padding-bottom: 2px;"><b>Hormat kami,</b></td></tr>
+ <tr><td style="padding-bottom: 2px;">PT. INDOTEKNIK DOTCOM GEMILANG</td></tr>
+ <tr>
+ <td valign="middle" align="left">
+ <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 50%; height: auto;"></img>
+ </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="action_user_pengajuan_tempo_request" model="ir.actions.act_window">
+ <field name="name">User Pengajuan Tempo Request</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">user.pengajuan.tempo.request</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_user_pengajuan_tempo_request"
+ name="User Pengajuan Tempo Request"
+ parent="res_partner_menu_user"
+ sequence="3"
+ action="action_user_pengajuan_tempo_request"
+ />
+</odoo> \ No newline at end of file