From 01507fad2a05124137541dc187a06631732c606e Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 11 Dec 2024 15:14:31 +0700 Subject: master vendor sla --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/vendor_sla.py | 26 ++++++++++++++++ indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/vendor_sla.xml | 42 ++++++++++++++++++++++++++ indoteknik_custom/views/x_banner_category.xml | 2 +- 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/vendor_sla.py create mode 100644 indoteknik_custom/views/vendor_sla.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 89f62524..5f77d2de 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -155,6 +155,7 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'views/vendor_sla.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index ad6d75dd..70a84bd0 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -134,3 +134,4 @@ from . import find_page from . import approval_retur_picking from . import va_multi_approve from . import va_multi_reject +from . import vendor_sla diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py new file mode 100644 index 00000000..1e0a344f --- /dev/null +++ b/indoteknik_custom/models/vendor_sla.py @@ -0,0 +1,26 @@ +from odoo import models, fields, api + +class VendorSLA(models.Model): + _name = 'vendor.sla' + _description = 'Vendor SLA' + _rec_name = 'id_vendor' + + id_vendor = fields.Many2one('res.partner', string='Name') + duration = fields.Integer(string='Duration', description='Duration SLA') + unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="Unit" + ) + duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") + + + @api.depends('duration', 'unit') + def _compute_duration_unit(self): + for record in self: + if record.duration and record.unit: + record.duration_unit = f"{record.duration} {record.unit}" + else: + record.duration_unit = "" + + + \ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 2375df9d..dcfee14b 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -148,3 +148,4 @@ access_sales_order_fulfillment_v2,access.sales.order.fulfillment.v2,model_sales_ 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_vendor_sla,access.vendor_sla,model_vendor_sla,,1,1,1,1 diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml new file mode 100644 index 00000000..d0e7f3e6 --- /dev/null +++ b/indoteknik_custom/views/vendor_sla.xml @@ -0,0 +1,42 @@ + + + + Vendor SLA + vendor.sla + tree,form + + + + Vendor SLA + vendor.sla + + + + + + + + + + Vendor SLA + vendor.sla + +
+ + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/indoteknik_custom/views/x_banner_category.xml b/indoteknik_custom/views/x_banner_category.xml index 11feb207..a83c4129 100755 --- a/indoteknik_custom/views/x_banner_category.xml +++ b/indoteknik_custom/views/x_banner_category.xml @@ -23,7 +23,7 @@ - + -- cgit v1.2.3 From 3ba967de0aa6c70b0d070a7ca723d49008aa0dd2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 13 Dec 2024 14:15:17 +0700 Subject: product sla --- indoteknik_custom/models/product_sla.py | 15 +++++++++++++++ indoteknik_custom/models/vendor_sla.py | 6 +++--- indoteknik_custom/views/product_sla.xml | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 2e663d30..05210cdc 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,6 +12,8 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') + vendor_id = fields.Many2one('res.partner',string='Vendor', readonly=True) + sla_vendor = fields.Char(string='SLA Vendor', readonly=True) avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) leadtime = fields.Char(string='Leadtime', readonly=True) sla = fields.Char(string='SLA', readonly=True) @@ -36,6 +38,7 @@ class ProductSla(models.Model): product.sla_version += 1 product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) + print(product_sla.id, product.id) if not product_sla: product_sla = self.env['product.sla'].create({ 'product_variant_id': product.id, @@ -48,6 +51,18 @@ class ProductSla(models.Model): self.sla = '-' product = self.product_variant_id + + q_vendor = [ + ('product_id', '=', product.id), + ('is_winner', '=', True) + ] + + vendor = self.env['purchase.pricelist'].search(q_vendor) + print(vendor.vendor_id.id) + if vendor: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + self.sla_vendor = "{} {}".format(vendor_sla.duration, vendor_sla.unit) + self.vendor_id = vendor.vendor_id.id qty_available = 0 qty_available = product.qty_onhand_bandengan diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 1e0a344f..9af86a14 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -5,11 +5,11 @@ class VendorSLA(models.Model): _description = 'Vendor SLA' _rec_name = 'id_vendor' - id_vendor = fields.Many2one('res.partner', string='Name') - duration = fields.Integer(string='Duration', description='Duration SLA') + id_vendor = fields.Many2one('res.partner', string='Name', domain="[('industry_id', '=', 46)]") + duration = fields.Integer(string='Duration', description='SLA Duration') unit = fields.Selection( [('jam', 'Jam'),('hari', 'Hari')], - string="Unit" + string="SLA Time" ) duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 8b0e874b..5276bb03 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,6 +6,8 @@ + + -- cgit v1.2.3 From a6acc141629f358f885f62e395986131c1ec1909 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 27 Dec 2024 11:44:21 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index d5cc7c5c..6ca9d419 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -26,4 +26,29 @@ class Lead(controller.Controller): lead = request.env['crm.lead'].create(params['value']) + return self.response(True) + + @http.route('/api/v1/merchant', auth='public', methods=['POST', 'OPTIONS'], csrf=False) + @controller.Controller.must_authorized() + def create_merchant(self, **kw): + params = self.get_request_params(kw, { + "name": ["required"], + "contact_name": [], + "email_from": [], + "phone": [], + "file_npwp": [], + "file_nib": [], + "file_tdp": [], + "file_siup": [], + "file_quotation": [], + "description": [] + }) + + if not params['valid']: + return self.response(code=400, description=params) + + # params['value']['user_id'] = 20 + + # lead = request.env['crm.lead'].create(params['value']) + return self.response(True) \ No newline at end of file -- cgit v1.2.3 From efe91d5ed3170b1d8e2dc217a05261dfbd9687d5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 27 Dec 2024 14:58:24 +0700 Subject: update mechant --- indoteknik_api/controllers/api_v1/lead.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index 6ca9d419..f0cfa1be 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -33,14 +33,26 @@ class Lead(controller.Controller): def create_merchant(self, **kw): params = self.get_request_params(kw, { "name": ["required"], - "contact_name": [], - "email_from": [], + "address": [], + "state": [], + "city": [], + "district": [], + "subDistrict": [], + "zip": [], + "bank_name": [], + "rekening_name": [], + "account_number": [], + "email_company": [], + "email_sales": [], + "email_finnance": [], "phone": [], + "mobile": [], + "file_dokumenKtpDirut ": [], + "file_kartuNama": [], "file_npwp": [], - "file_nib": [], - "file_tdp": [], - "file_siup": [], - "file_quotation": [], + "file_sppkp": [], + "file_suratPernyataan": [], + "file_fotoKantor": [], "description": [] }) -- cgit v1.2.3 From 7d5204a92422848f617af2d0e50d7069bf9f7824 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 30 Dec 2024 14:09:50 +0700 Subject: add form merchant --- indoteknik_api/controllers/api_v1/lead.py | 22 +++-- indoteknik_custom/__manifest__.py | 2 + indoteknik_custom/models/__init__.py | 2 + indoteknik_custom/models/res_partner.py | 76 ++++++++++++++- indoteknik_custom/models/user_form_merchant.py | 42 ++++++++ indoteknik_custom/models/user_merchant_request.py | 96 +++++++++++++++++++ indoteknik_custom/security/ir.model.access.csv | 4 + indoteknik_custom/views/res_partner.xml | 38 ++++++++ indoteknik_custom/views/user_form_merchant.xml | 77 +++++++++++++++ indoteknik_custom/views/user_merchant_request.xml | 112 ++++++++++++++++++++++ 10 files changed, 464 insertions(+), 7 deletions(-) create mode 100644 indoteknik_custom/models/user_form_merchant.py create mode 100644 indoteknik_custom/models/user_merchant_request.py create mode 100644 indoteknik_custom/views/user_form_merchant.xml create mode 100644 indoteknik_custom/views/user_merchant_request.xml diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index f0cfa1be..7a964b21 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -32,7 +32,7 @@ class Lead(controller.Controller): @controller.Controller.must_authorized() def create_merchant(self, **kw): params = self.get_request_params(kw, { - "name": ["required"], + "name_merchant": ["required"], "address": [], "state": [], "city": [], @@ -44,7 +44,7 @@ class Lead(controller.Controller): "account_number": [], "email_company": [], "email_sales": [], - "email_finnance": [], + "email_finance": [], "phone": [], "mobile": [], "file_dokumenKtpDirut ": [], @@ -53,14 +53,24 @@ class Lead(controller.Controller): "file_sppkp": [], "file_suratPernyataan": [], "file_fotoKantor": [], - "description": [] + "description": [], }) + filtered_params = {key: value for key, value in params['value'].items() if value} + lead = request.env['user.form.merchant'].create(filtered_params) + + partner_id = int(kw.get('partner_id')) + + partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) if not params['valid']: return self.response(code=400, description=params) - # params['value']['user_id'] = 20 + main_partner = partner.get_main_parent() - # lead = request.env['crm.lead'].create(params['value']) + user_merchant_request = request.env['user.merchant.request'].create({ + 'user_id': partner.id, + 'merchant_id': lead.id, + 'user_company_id': main_partner.id + }) - return self.response(True) \ No newline at end of file + return self.response(True) diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 89f62524..b0f29747 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -30,6 +30,8 @@ 'views/web_logging/user_activity_log.xml', 'views/web_logging/web_utm_source.xml', 'views/user_company_request.xml', + 'views/user_form_merchant.xml', + 'views/user_merchant_request.xml', 'views/vit_kelurahan.xml', 'views/vit_kecamatan.xml', 'views/vit_kota.xml', diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index ad6d75dd..25f56052 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -134,3 +134,5 @@ from . import find_page from . import approval_retur_picking from . import va_multi_approve from . import va_multi_reject +from . import user_form_merchant +from . import user_merchant_request diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index da4a6cb6..79962c2b 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -75,6 +75,30 @@ class ResPartner(models.Model): "Set its value to 0.00 to disable " "this feature", tracking=3) + # MERCHANT + name_merchant = fields.Char(string='Name') + address_merchant = fields.Char(string='Alamat') + state_merchant = fields.Many2one('res.country.state', string='State') + city_merchant = fields.Many2one('vit.kota', string='Kota') + district_merchant = fields.Many2one('vit.kecamatan', string='Kecamatan') + subDistrict_merchant = fields.Many2one('vit.kelurahan', string='Kelurahan') + zip_merchant = fields.Char(string='Kode Pos') + bank_name_merchant = fields.Char(string='Nama Bank') + rekening_name_merchant = fields.Char(string='Nama Rekening') + account_number_merchant = fields.Char(string='Nomor Rekening Bank') + email_company_merchant = fields.Char(string='Email Perusahaan') + email_sales_merchant = fields.Char(string='Email Sales') + email_finance_merchant = fields.Char(string='Email Finance') + phone_merchant = fields.Char(string='No. Telepon Perusahaan') + mobile_merchant = fields.Char(string='No. Handphone') + file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") + file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") + file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") + file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") + file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, track_visibility="onchange") + file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") + description = fields.Text(string='Deskripsi') + @api.model def _default_payment_term(self): return self.env.ref('__export__.account_payment_term_26_484409e2').id @@ -136,6 +160,31 @@ class ResPartner(models.Model): vals['company_type_id'] = vals.get('company_type_id', self.company_type_id.id if self.company_type_id else None) + # Merchant + vals['name_merchant'] = vals.get('name_merchant', self.name_merchant) + vals['address_merchant'] = vals.get('address_merchant', self.address_merchant) + vals['state_merchant'] = vals.get('state_merchant', self.state_merchant) + vals['city_merchant'] = vals.get('city_merchant', self.city_merchant) + vals['district_merchant'] = vals.get('district_merchant', self.district_merchant) + vals['subDistrict_merchant'] = vals.get('subDistrict_merchant', self.subDistrict_merchant) + vals['zip_merchant'] = vals.get('zip_merchant', self.zip_merchant) + vals['bank_name_merchant'] = vals.get('bank_name_merchant', self.bank_name_merchant) + vals['rekening_name_merchant'] = vals.get('rekening_name_merchant', self.rekening_name_merchant) + vals['account_number_merchant'] = vals.get('account_number_merchant', self.account_number_merchant) + vals['email_company_merchant'] = vals.get('email_company_merchant', self.email_company_merchant) + vals['email_sales_merchant'] = vals.get('email_sales_merchant', self.email_sales_merchant) + vals['email_finance_merchant'] = vals.get('email_finance_merchant', self.email_finance_merchant) + vals['phone_merchant'] = vals.get('phone_merchant', self.phone_merchant) + vals['mobile_merchant'] = vals.get('mobile_merchant', self.mobile_merchant) + vals['file_dokumenKtpDirut'] = vals.get('file_dokumenKtpDirut', self.file_dokumenKtpDirut) + vals['file_kartuNama'] = vals.get('file_kartuNama', self.file_kartuNama) + vals['file_npwp'] = vals.get('file_npwp', self.file_npwp) + vals['file_sppkp'] = vals.get('file_sppkp', self.file_sppkp) + vals['file_suratPernyataan'] = vals.get('file_suratPernyataan', self.file_suratPernyataan) + vals['file_fotoKantor'] = vals.get('file_fotoKantor', self.file_fotoKantor) + vals['description'] = vals.get('description', self.description) + + # Simpan hanya field yang perlu di-update pada child vals_for_child = { 'customer_type': vals.get('customer_type'), @@ -144,7 +193,32 @@ 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'), + + # Merchant + 'name_merchant': vals.get('name_merchant'), + 'address_merchant': vals.get('address_merchant'), + 'state_merchant': vals.get('state_merchant'), + 'city_merchant': vals.get('city_merchant'), + 'district_merchant': vals.get('district_merchant'), + 'subDistrict_merchant': vals.get('subDistrict_merchant'), + 'zip_merchant': vals.get('zip_merchant'), + 'bank_name_merchant': vals.get('bank_name_merchant'), + 'rekening_name_merchant': vals.get('rekening_name_merchant'), + 'account_number_merchant': vals.get('account_number_merchant'), + 'email_company_merchant': vals.get('email_company_merchant'), + 'email_sales_merchant': vals.get('email_sales_merchant'), + 'email_finance_merchant': vals.get('email_finance_merchant'), + 'phone_merchant': vals.get('phone_merchant'), + 'mobile_merchant': vals.get('mobile_merchant'), + 'file_dokumenKtpDirut': vals.get('file_dokumenKtpDirut'), + 'file_kartuNama': vals.get('file_kartuNama'), + 'file_npwp': vals.get('file_npwp'), + 'file_sppkp': vals.get('file_sppkp'), + 'file_suratPernyataan': vals.get('file_suratPernyataan'), + 'file_fotoKantor': vals.get('file_fotoKantor'), + 'description': vals.get('description'), + } # Lakukan update pada semua child secara rekursif diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py new file mode 100644 index 00000000..96568f6c --- /dev/null +++ b/indoteknik_custom/models/user_form_merchant.py @@ -0,0 +1,42 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError +from odoo.http import request + + +class UserFormMerchant(models.Model): + _name = 'user.form.merchant' + _inherit = ['mail.thread', 'mail.activity.mixin'] + name = fields.Char(string='Name', required=True) + name_merchant = fields.Char(string='Name') + address = fields.Char(string='Alamat') + state = fields.Many2one('res.country.state', string='State') + city = fields.Many2one('vit.kota', string='Kota') + district = fields.Many2one('vit.kecamatan', string='Kecamatan') + subDistrict = fields.Many2one('vit.kelurahan', string='Kelurahan') + zip = fields.Char(string='Kode Pos') + bank_name = fields.Char(string='Nama Bank') + rekening_name = fields.Char(string='Nama Rekening') + account_number = fields.Char(string='Nomor Rekening Bank') + email_company = fields.Char(string='Email Perusahaan') + email_sales = fields.Char(string='Email Sales') + email_finance = fields.Char(string='Email Finance') + phone = fields.Char(string='No. Telepon Perusahaan') + mobile = fields.Char(string='No. Handphone') + file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") + file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") + file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") + file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") + file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, track_visibility="onchange") + file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") + description = fields.Text(string='Deskripsi') + + @api.depends('name', 'name_merchant') + def name_get(self): + result = [] + for record in self: + if record.name_merchant: + display_name = record.name_merchant + else: + display_name = "DETAIL FORM MERCHANT" + result.append((record.id, display_name)) + return result \ No newline at end of file diff --git a/indoteknik_custom/models/user_merchant_request.py b/indoteknik_custom/models/user_merchant_request.py new file mode 100644 index 00000000..b47f6d1d --- /dev/null +++ b/indoteknik_custom/models/user_merchant_request.py @@ -0,0 +1,96 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError +from odoo.http import request + + +class RejectReasonWizardMerchant(models.TransientModel): + _name = 'reject.reason.wizard.merchant' + _description = 'Wizard for Reject Reason' + + request_id = fields.Many2one('user.merchant.request', string='Request') + reason_reject = fields.Text(string='Reason for Rejection', required=True) + + def confirm_reject(self): + merchant = self.request_id + if merchant: + merchant.write({'reason_reject': self.reason_reject}) + merchant.state_merchant = 'reject' + return {'type': 'ir.actions.act_window_close'} + + +class ConfirmApprovalWizardMerchant(models.TransientModel): + _name = 'confirm.approval.wizard.merchant' + _description = 'Wizard Konfirmasi Approval' + + merchant_id = fields.Many2one('user.merchant.request', string='Merchant', required=True) + + def confirm_approval(self): + merchant = self.merchant_id + if merchant.state_merchant == 'draft': + merchant.state_merchant = 'approved' + + +class UserMerchantRequest(models.Model): + _name = 'user.merchant.request' + _inherit = ['mail.thread', 'mail.activity.mixin'] + _rec_name = 'user_id' + + user_id = fields.Many2one('res.partner', string='User') + merchant_id = fields.Many2one('user.form.merchant', string='Form Merchant') + user_company_id = fields.Many2one('res.partner', string='Company') + state_merchant = fields.Selection([ + ('draft', 'Pengajuan Merchant'), + ('approved', 'Approved Merchant'), + ('reject', 'Rejected'), + ], string='Status', readonly=True, copy=False, index=True, track_visibility='onchange', default='draft') + reason_reject = fields.Char(string='Reaject Reason') + + def button_approve(self): + for merchant in self: + return { + 'type': 'ir.actions.act_window', + 'name': 'Konfirmasi Approve', + 'res_model': 'confirm.approval.wizard.merchant', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_merchant_id': merchant.id, + }} + + def button_reject(self): + return { + 'type': 'ir.actions.act_window', + 'name': _('Reject Reason'), + 'res_model': 'reject.reason.wizard.merchant', + 'view_mode': 'form', + 'target': 'new', + 'context': {'default_request_id': self.id}, + } + + def write(self, vals): + is_approve = True if self.state_merchant == 'approved' or vals.get('state_merchant') == 'approved' else False + if is_approve: + self.user_company_id.name_merchant = self.merchant_id.name_merchant + self.user_company_id.address_merchant = self.merchant_id.address + self.user_company_id.state_merchant = self.merchant_id.state + self.user_company_id.city_merchant = self.merchant_id.city + self.user_company_id.district_merchant = self.merchant_id.district + self.user_company_id.subDistrict_merchant = self.merchant_id.subDistrict + self.user_company_id.zip_merchant = self.merchant_id.zip + self.user_company_id.bank_name_merchant = self.merchant_id.bank_name + self.user_company_id.rekening_name_merchant = self.merchant_id.rekening_name + self.user_company_id.account_number_merchant = self.merchant_id.account_number + self.user_company_id.email_company_merchant = self.merchant_id.email_company + self.user_company_id.email_sales_merchant = self.merchant_id.email_sales + self.user_company_id.email_finance_merchant = self.merchant_id.email_finance + self.user_company_id.phone_merchant = self.merchant_id.phone + self.user_company_id.mobile_merchant = self.merchant_id.mobile + self.user_company_id.file_dokumenKtpDirut = self.merchant_id.file_dokumenKtpDirut + self.user_company_id.file_kartuNama = self.merchant_id.file_kartuNama + self.user_company_id.file_npwp = self.merchant_id.file_npwp + self.user_company_id.file_sppkp = self.merchant_id.file_sppkp + self.user_company_id.file_suratPernyataan = self.merchant_id.file_suratPernyataan + self.user_company_id.file_fotoKantor = self.merchant_id.file_fotoKantor + self.user_company_id.description = self.merchant_id.description + + return super(UserMerchantRequest, self).write(vals) \ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 2375df9d..7bf84d2c 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -148,3 +148,7 @@ access_sales_order_fulfillment_v2,access.sales.order.fulfillment.v2,model_sales_ 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_user_form_merchant,access.user.form.merchant,model_user_form_merchant,,1,1,1,1 +access_user_merchant_request,access.user.merchant.request,model_user_merchant_request,,1,1,1,1 +access_reject_reason_wizard_merchant,reject.reason.wizard.merchant,model_reject_reason_wizard_merchant,,1,1,1,0 +access_confirm_approval_wizard_merchant,confirm.approval.wizard.merchant,model_confirm_approval_wizard_merchant,,1,1,1,0 diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 7b13fef7..5fcc7de5 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -80,6 +80,44 @@ 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml new file mode 100644 index 00000000..f4f91933 --- /dev/null +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -0,0 +1,77 @@ + + + + user.form.merchant.tree + user.form.merchant + + + + + + + + + + + + + user.form.merchant.form + user.form.merchant + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + User Form Merchant + ir.actions.act_window + user.form.merchant + tree,form + + + + +
\ No newline at end of file diff --git a/indoteknik_custom/views/user_merchant_request.xml b/indoteknik_custom/views/user_merchant_request.xml new file mode 100644 index 00000000..c67daae4 --- /dev/null +++ b/indoteknik_custom/views/user_merchant_request.xml @@ -0,0 +1,112 @@ + + + + user.merchant.request.tree + user.merchant.request + + + + + + + + + + + + user.merchant.request.form + user.merchant.request + +
+
+
+ + + + + + + + +
+ +
+
+
+
+ + + + + + + reject.reason.wizard.merchant.form + reject.reason.wizard.merchant + +
+ + + +
+
+
+
+
+ + + Reject Reason + reject.reason.wizard.merchant + form + new + + + + + confirm.approval.wizard.merchant.form + confirm.approval.wizard.merchant + +
+ +

Apakah Anda yakin ingin mengapprove merchant ini?

+
+
+
+
+
+
+ + + + + User Merchant Request + ir.actions.act_window + user.merchant.request + tree,form + + + +
\ No newline at end of file -- cgit v1.2.3 From 60f54931f8eac477ab737abab1710789e0a2aaf4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 30 Dec 2024 16:30:20 +0700 Subject: vendor sla --- indoteknik_custom/models/product_sla.py | 30 ++++++++++++++++++++++-------- indoteknik_custom/views/vendor_sla.xml | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 05210cdc..f597ec44 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -25,6 +25,7 @@ class ProductSla(models.Model): def generate_product_variant_id_sla(self, limit=5000): # Filter produk non-Altama + products = self.env['product.product'].search([ ('x_manufacture', 'not in', [10, 122, 89]), ('location_id', '=', 57), @@ -58,10 +59,19 @@ class ProductSla(models.Model): ] vendor = self.env['purchase.pricelist'].search(q_vendor) + + vendor_duration = 0 print(vendor.vendor_id.id) if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) - self.sla_vendor = "{} {}".format(vendor_sla.duration, vendor_sla.unit) + sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 + if sla_vendor > 0: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration + + self.sla_vendor = vendor_sla.duration_unit self.vendor_id = vendor.vendor_id.id qty_available = 0 @@ -90,10 +100,14 @@ class ProductSla(models.Model): if len(leadtimes) > 0: avg_leadtime = sum(leadtimes) / len(leadtimes) rounded_leadtime = math.ceil(avg_leadtime) - self.avg_leadtime = rounded_leadtime - if rounded_leadtime >= 1 and rounded_leadtime <= 5: - self.sla = '3-7 Hari' - elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - self.sla = '4-12 Hari' - elif rounded_leadtime >= 11: - self.sla = 'Indent' \ No newline at end of file + estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = estimation_sla_days + self.avg_leadtime = int(rounded_leadtime) + # self.sla = (sla_vendor + self.avg_leadtime) / 2 + # if rounded_leadtime >= 1 and rounded_leadtime <= 5: + # self.sla = '3-7 Hari' + # elif rounded_leadtime >= 6 and rounded_leadtime <= 10: + # self.sla = '4-12 Hari' + # elif rounded_leadtime >= 11: + # self.sla = 'Indent' \ No newline at end of file diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml index d0e7f3e6..cf4425eb 100644 --- a/indoteknik_custom/views/vendor_sla.xml +++ b/indoteknik_custom/views/vendor_sla.xml @@ -26,7 +26,7 @@ - + -- cgit v1.2.3 From b2b458d4a642271b98f23403c74eb7aad06cd71d Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 2 Jan 2025 08:52:13 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 4 ++++ indoteknik_api/models/res_users.py | 1 + indoteknik_custom/models/res_partner.py | 8 ++++++++ indoteknik_custom/models/user_form_merchant.py | 4 ++++ indoteknik_custom/models/user_merchant_request.py | 4 ++++ indoteknik_custom/views/res_partner.xml | 16 ++++++++++------ indoteknik_custom/views/user_form_merchant.xml | 16 ++++++++++------ 7 files changed, 41 insertions(+), 12 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index 7a964b21..61a37dc6 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -33,6 +33,7 @@ class Lead(controller.Controller): def create_merchant(self, **kw): params = self.get_request_params(kw, { "name_merchant": ["required"], + "pic_merchant":[], "address": [], "state": [], "city": [], @@ -47,12 +48,15 @@ class Lead(controller.Controller): "email_finance": [], "phone": [], "mobile": [], + "harga_tayang": [], "file_dokumenKtpDirut ": [], "file_kartuNama": [], "file_npwp": [], "file_sppkp": [], "file_suratPernyataan": [], "file_fotoKantor": [], + "file_dataProduk": [], + "file_pricelist": [], "description": [], }) filtered_params = {key: value for key, value in params['value'].items() if value} diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py index 77aeeef7..37441d7f 100644 --- a/indoteknik_api/models/res_users.py +++ b/indoteknik_api/models/res_users.py @@ -63,6 +63,7 @@ class ResUsers(models.Model): 'rajaongkir_city_id': user.kecamatan_id.rajaongkir_id or 0, 'alamat_wajib_pajak': user.alamat_lengkap_text or None, 'alamat_bisnis': user.street or None, + 'companyType': user.customer_type or 'nonpkp', 'longtitude': user.longtitude or None, 'latitude': user.latitude or None, 'address_map': user.address_map or None, diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 79962c2b..9e53dae1 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -77,6 +77,7 @@ class ResPartner(models.Model): # MERCHANT name_merchant = fields.Char(string='Name') + pic_merchant = fields.Char(string='PIC Merchant', required=True) address_merchant = fields.Char(string='Alamat') state_merchant = fields.Many2one('res.country.state', string='State') city_merchant = fields.Many2one('vit.kota', string='Kota') @@ -91,12 +92,15 @@ class ResPartner(models.Model): email_finance_merchant = fields.Char(string='Email Finance') phone_merchant = fields.Char(string='No. Telepon Perusahaan') mobile_merchant = fields.Char(string='No. Handphone') + harga_tayang = fields.Char(string='Harga Tayang (HET)') file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, track_visibility="onchange") file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") + file_dataProduk = fields.Binary(string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=True,track_visibility="onchange") + file_pricelist = fields.Binary(string="Pricelist", tracking=True, track_visibility="onchange") description = fields.Text(string='Deskripsi') @api.model @@ -162,6 +166,7 @@ class ResPartner(models.Model): # Merchant vals['name_merchant'] = vals.get('name_merchant', self.name_merchant) + vals['pic_merchant'] = vals.get('pic_merchant', self.pic_merchant) vals['address_merchant'] = vals.get('address_merchant', self.address_merchant) vals['state_merchant'] = vals.get('state_merchant', self.state_merchant) vals['city_merchant'] = vals.get('city_merchant', self.city_merchant) @@ -176,12 +181,15 @@ class ResPartner(models.Model): vals['email_finance_merchant'] = vals.get('email_finance_merchant', self.email_finance_merchant) vals['phone_merchant'] = vals.get('phone_merchant', self.phone_merchant) vals['mobile_merchant'] = vals.get('mobile_merchant', self.mobile_merchant) + vals['harga_tayang'] = vals.get('harga_tayang', self.harga_tayang) vals['file_dokumenKtpDirut'] = vals.get('file_dokumenKtpDirut', self.file_dokumenKtpDirut) vals['file_kartuNama'] = vals.get('file_kartuNama', self.file_kartuNama) vals['file_npwp'] = vals.get('file_npwp', self.file_npwp) vals['file_sppkp'] = vals.get('file_sppkp', self.file_sppkp) vals['file_suratPernyataan'] = vals.get('file_suratPernyataan', self.file_suratPernyataan) vals['file_fotoKantor'] = vals.get('file_fotoKantor', self.file_fotoKantor) + vals['file_dataProduk'] = vals.get('file_dataProduk', self.file_dataProduk) + vals['file_pricelist'] = vals.get('file_pricelist', self.file_pricelist) vals['description'] = vals.get('description', self.description) diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 96568f6c..31c85481 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -7,6 +7,7 @@ class UserFormMerchant(models.Model): _name = 'user.form.merchant' _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name', required=True) + pic_merchant = fields.Char(string='PIC Merchant', required=True) name_merchant = fields.Char(string='Name') address = fields.Char(string='Alamat') state = fields.Many2one('res.country.state', string='State') @@ -22,12 +23,15 @@ class UserFormMerchant(models.Model): email_finance = fields.Char(string='Email Finance') phone = fields.Char(string='No. Telepon Perusahaan') mobile = fields.Char(string='No. Handphone') + harga_tayang = fields.Char(string='Harga Tayang (HET)') file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, track_visibility="onchange") file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") + file_dataProduk = fields.Binary(string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=True, track_visibility="onchange") + file_pricelist = fields.Binary(string="Pricelist", tracking=True, track_visibility="onchange") description = fields.Text(string='Deskripsi') @api.depends('name', 'name_merchant') diff --git a/indoteknik_custom/models/user_merchant_request.py b/indoteknik_custom/models/user_merchant_request.py index b47f6d1d..7f6c5f3c 100644 --- a/indoteknik_custom/models/user_merchant_request.py +++ b/indoteknik_custom/models/user_merchant_request.py @@ -71,6 +71,7 @@ class UserMerchantRequest(models.Model): is_approve = True if self.state_merchant == 'approved' or vals.get('state_merchant') == 'approved' else False if is_approve: self.user_company_id.name_merchant = self.merchant_id.name_merchant + self.user_company_id.pic_merchant = self.merchant_id.pic_merchant self.user_company_id.address_merchant = self.merchant_id.address self.user_company_id.state_merchant = self.merchant_id.state self.user_company_id.city_merchant = self.merchant_id.city @@ -85,12 +86,15 @@ class UserMerchantRequest(models.Model): self.user_company_id.email_finance_merchant = self.merchant_id.email_finance self.user_company_id.phone_merchant = self.merchant_id.phone self.user_company_id.mobile_merchant = self.merchant_id.mobile + self.user_company_id.harga_tayang = self.merchant_id.harga_tayang self.user_company_id.file_dokumenKtpDirut = self.merchant_id.file_dokumenKtpDirut self.user_company_id.file_kartuNama = self.merchant_id.file_kartuNama self.user_company_id.file_npwp = self.merchant_id.file_npwp self.user_company_id.file_sppkp = self.merchant_id.file_sppkp self.user_company_id.file_suratPernyataan = self.merchant_id.file_suratPernyataan self.user_company_id.file_fotoKantor = self.merchant_id.file_fotoKantor + self.user_company_id.file_dataProduk = self.merchant_id.file_dataProduk + self.user_company_id.file_pricelist = self.merchant_id.file_pricelist self.user_company_id.description = self.merchant_id.description return super(UserMerchantRequest, self).write(vals) \ No newline at end of file diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 5fcc7de5..d3ef2657 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -85,6 +85,7 @@ + @@ -98,12 +99,14 @@ - - - - - - + + + + + + + + @@ -111,6 +114,7 @@ + diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml index f4f91933..c30f2756 100644 --- a/indoteknik_custom/views/user_form_merchant.xml +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -23,6 +23,7 @@ + @@ -36,12 +37,14 @@ - - - - - - + + + + + + + + @@ -49,6 +52,7 @@ + -- cgit v1.2.3 From bd6db8fa22dd602cfaffe0a4b44cc2a794fa1975 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 2 Jan 2025 14:18:46 +0700 Subject: update merchant menu --- indoteknik_custom/models/user_form_merchant.py | 2 +- indoteknik_custom/views/user_company_request.xml | 19 ++++++++++++------- indoteknik_custom/views/user_form_merchant.xml | 5 +++-- indoteknik_custom/views/user_merchant_request.xml | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 31c85481..18d82e25 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -6,7 +6,7 @@ from odoo.http import request class UserFormMerchant(models.Model): _name = 'user.form.merchant' _inherit = ['mail.thread', 'mail.activity.mixin'] - name = fields.Char(string='Name', required=True) + name = fields.Char(string='Name') pic_merchant = fields.Char(string='PIC Merchant', required=True) name_merchant = fields.Char(string='Name') address = fields.Char(string='Alamat') diff --git a/indoteknik_custom/views/user_company_request.xml b/indoteknik_custom/views/user_company_request.xml index 1c80e923..a4a9d842 100644 --- a/indoteknik_custom/views/user_company_request.xml +++ b/indoteknik_custom/views/user_company_request.xml @@ -54,12 +54,17 @@ user.company.request tree,form + + + - \ No newline at end of file diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml index c30f2756..230b6425 100644 --- a/indoteknik_custom/views/user_form_merchant.xml +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -70,11 +70,12 @@ tree,form + diff --git a/indoteknik_custom/views/user_merchant_request.xml b/indoteknik_custom/views/user_merchant_request.xml index c67daae4..e4f309fd 100644 --- a/indoteknik_custom/views/user_merchant_request.xml +++ b/indoteknik_custom/views/user_merchant_request.xml @@ -105,8 +105,8 @@ \ No newline at end of file -- cgit v1.2.3 From 7ac434ec0fcf75cb6eefe1892118b7c18b3db53a Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 7 Jan 2025 17:02:06 +0700 Subject: sla --- indoteknik_api/controllers/api_v1/product.py | 23 ++++++++++----- indoteknik_custom/models/product_sla.py | 44 ++++++++++++---------------- indoteknik_custom/views/product_sla.xml | 4 +-- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 32362582..2573d7a8 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -35,7 +35,7 @@ class Product(controller.Controller): return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) - @controller.Controller.must_authorized() + @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): id = int(kw.get('id')) date_7_days_ago = datetime.now() - timedelta(days=7) @@ -47,12 +47,19 @@ class Product(controller.Controller): ('product_variant_id', '=', id), ('write_date', '>=', date_7_days_ago.strftime("%Y-%m-%d %H:%M:%S")) ], limit=1) + + include_instant = False qty_available = product.qty_free_bandengan - - if qty_available < 0: - qty_available = 0 - + + + if qty_available > 0 : + include_instant = True + else : + qty_available = 0 + if product_sla.sla_vendor_id.unit == 'jam': + include_instant = True + qty = 0 sla_date = '-' @@ -74,9 +81,10 @@ class Product(controller.Controller): if qty_available > 0: qty = qty_available + total_adem + total_excell + sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = '2-4 Hari' + sla_date = product.sla else: sla_date = '3-7 Hari' except: @@ -84,7 +92,7 @@ class Product(controller.Controller): else: if qty_available > 0: qty = qty_available - sla_date = product_sla.sla or '-' + sla_date = product_sla.sla or 'Indent' elif qty_vendor > 0: qty = total_excell sla_date = '2-4 Hari' @@ -92,6 +100,7 @@ class Product(controller.Controller): data = { 'qty': qty, 'sla_date': sla_date, + 'can_instant': include_instant } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index f597ec44..dfdf7662 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,8 +12,8 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') - vendor_id = fields.Many2one('res.partner',string='Vendor', readonly=True) - sla_vendor = fields.Char(string='SLA Vendor', readonly=True) + sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) + sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) leadtime = fields.Char(string='Leadtime', readonly=True) sla = fields.Char(string='SLA', readonly=True) @@ -52,7 +52,14 @@ class ProductSla(models.Model): self.sla = '-' product = self.product_variant_id + + # qty_available = 0 + # qty_available = product.qty_onhand_bandengan + + # if qty_available > 0: + # self.sla = 1 + q_vendor = [ ('product_id', '=', product.id), ('is_winner', '=', True) @@ -61,7 +68,6 @@ class ProductSla(models.Model): vendor = self.env['purchase.pricelist'].search(q_vendor) vendor_duration = 0 - print(vendor.vendor_id.id) if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 @@ -69,17 +75,9 @@ class ProductSla(models.Model): if vendor_sla.unit == 'hari': vendor_duration = vendor_sla.duration * 24 * 60 else : - vendor_duration = vendor_sla.duration - - self.sla_vendor = vendor_sla.duration_unit - self.vendor_id = vendor.vendor_id.id - - qty_available = 0 - qty_available = product.qty_onhand_bandengan - - - if qty_available > 0: - self.sla = '1 Hari' + vendor_duration = vendor_sla.duration * 60 + + self.sla_vendor_id = vendor_sla.id if vendor_sla else False query = [ ('product_id', '=', product.id), @@ -88,12 +86,13 @@ class ProductSla(models.Model): ('picking_id.state', 'not in', ['cancel']) ] picking = self.env['stock.move.line'].search(query) + leadtimes=[] for stock in picking: date_delivered = stock.picking_id.driver_departure_date - date_so_confirmed = stock.picking_id.sale_id.date_order - if date_delivered and date_so_confirmed: - leadtime = date_delivered - date_so_confirmed + date_do_ready = stock.picking_id.date_reserved + if date_delivered and date_do_ready: + leadtime = date_delivered - date_do_ready leadtime_in_days = leadtime.days leadtimes.append(leadtime_in_days) @@ -102,12 +101,5 @@ class ProductSla(models.Model): rounded_leadtime = math.ceil(avg_leadtime) estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration estimation_sla_days = estimation_sla / (24 * 60) - self.sla = estimation_sla_days - self.avg_leadtime = int(rounded_leadtime) - # self.sla = (sla_vendor + self.avg_leadtime) / 2 - # if rounded_leadtime >= 1 and rounded_leadtime <= 5: - # self.sla = '3-7 Hari' - # elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - # self.sla = '4-12 Hari' - # elif rounded_leadtime >= 11: - # self.sla = 'Indent' \ No newline at end of file + self.sla = math.ceil(estimation_sla_days) + self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 5276bb03..3722ef3d 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,8 +6,8 @@ - - + + -- cgit v1.2.3 From 63878bd84a6eb9094e702963d7c78fcd8dfa1808 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 10 Jan 2025 10:44:59 +0700 Subject: api sla --- indoteknik_api/controllers/api_v1/product.py | 59 ++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 2573d7a8..7570015f 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -1,6 +1,6 @@ from .. import controller from odoo import http -from odoo.http import request +from odoo.http import request, Response from datetime import datetime, timedelta import ast import logging @@ -33,7 +33,58 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - + + @http.route(prefix + 'product/variants/sla', auth='none', type='json', csrf=False, cors='*', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def get_product_template_sla_by_id(self, **kw): + json_raw = json.loads(request.httprequest.data) + + ids = json_raw.get('ids') + ids = list(map(int, ids)) + + if not ids or not isinstance(ids, list): + return ({'status' : 'Failed','message': 'Parameter "ids" harus berupa list dan tidak boleh kosong.'}) + + sla_days = 0 + products = request.env['product.product'].search([('id', 'in', ids)]) + if len(products) < 1: + return ({ + 'status' : 'Failed', + 'message' : 'Produk Tidak Di Temukan.' + }) + + product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) + if len(product_slas) < 1: + return ({ + 'status' : 'Failed', + 'message' : 'Produk Tidak Di Temukan.' + }) + + # Mapping SLA untuk mempermudah lookup + sla_map = {sla.product_variant_id.id: sla for sla in product_slas} + + for product in products: + product_sla = sla_map.get(product.id) + if product_sla: + sla_days = max(sla_days, product_sla.sla_vendor_id.duration) + if product.qty_free_bandengan < 1 : + if product_sla.sla_vendor_id.unit != 'jam': + return ({ + 'status' : 'Success', + 'data' : [{ + 'include_instant': False, + 'sla_days': sla_days + }], + }) + # Jika semua loop selesai tanpa include_instant menjadi False + return ({ + 'status' : 'Success', + 'data' : [{ + 'include_instant': True, + 'sla_days': sla_days + }], + }) + @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): @@ -84,9 +135,9 @@ class Product(controller.Controller): sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = product.sla + sla_date = product_sla.sla else: - sla_date = '3-7 Hari' + sla_date = product_sla.sla except: print('error') else: -- cgit v1.2.3 From 7b3a5a327bcae3b70d2e0bead9921f069c574647 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 13 Jan 2025 15:52:22 +0700 Subject: update code --- indoteknik_custom/models/user_form_merchant.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 18d82e25..c8025aa9 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -6,9 +6,14 @@ from odoo.http import request class UserFormMerchant(models.Model): _name = 'user.form.merchant' _inherit = ['mail.thread', 'mail.activity.mixin'] + name = fields.Char(string='Name') - pic_merchant = fields.Char(string='PIC Merchant', required=True) + # informasi peruhaan name_merchant = fields.Char(string='Name') + pejabat_name = fields.Char(string='Pejabat Name') + pic_merchant = fields.Char(string='PIC Merchant') + pic_position = fields.Char(string='Jabatan PIC') + partner_id = fields.Many2one('res.partner', string='Company') address = fields.Char(string='Alamat') state = fields.Many2one('res.country.state', string='State') city = fields.Many2one('vit.kota', string='Kota') @@ -23,6 +28,9 @@ class UserFormMerchant(models.Model): email_finance = fields.Char(string='Email Finance') phone = fields.Char(string='No. Telepon Perusahaan') mobile = fields.Char(string='No. Handphone') + bisnis_type = fields.Char(string='Bisnis Type') + category_perusahaan = fields.Char(string='Kategory Perusahaan') + description = fields.Text(string='Deskripsi') harga_tayang = fields.Char(string='Harga Tayang (HET)') file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") @@ -32,7 +40,6 @@ class UserFormMerchant(models.Model): file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") file_dataProduk = fields.Binary(string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=True, track_visibility="onchange") file_pricelist = fields.Binary(string="Pricelist", tracking=True, track_visibility="onchange") - description = fields.Text(string='Deskripsi') @api.depends('name', 'name_merchant') def name_get(self): -- cgit v1.2.3 From ef3ed689e8c8dc1de79e3744055fa12a97aefd35 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 13 Jan 2025 17:03:43 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 41 ++++++++++++++++---------- indoteknik_custom/models/user_form_merchant.py | 21 ++++++++++++- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index 61a37dc6..5749f5e5 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -31,9 +31,13 @@ class Lead(controller.Controller): @http.route('/api/v1/merchant', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def create_merchant(self, **kw): + merchant_request = True if kw.get('merchant_request') == 'true' else False params = self.get_request_params(kw, { - "name_merchant": ["required"], - "pic_merchant":[], + # informasi perusahaan + "name_merchant": [], + "pejabat_name": [], + "pic_merchant": [], + "pic_position": [], "address": [], "state": [], "city": [], @@ -48,6 +52,11 @@ class Lead(controller.Controller): "email_finance": [], "phone": [], "mobile": [], + "bisnis_type": [], + "category_perusahaan": [], + "description": [], + + # informasi vendor "harga_tayang": [], "file_dokumenKtpDirut ": [], "file_kartuNama": [], @@ -57,24 +66,26 @@ class Lead(controller.Controller): "file_fotoKantor": [], "file_dataProduk": [], "file_pricelist": [], - "description": [], }) - filtered_params = {key: value for key, value in params['value'].items() if value} - lead = request.env['user.form.merchant'].create(filtered_params) - partner_id = int(kw.get('partner_id')) - partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) + main_partner = partner.get_main_parent() + + filtered_params = {key: value for key, value in params['value'].items() if value} + form_merchant = request.env['user.form.merchant'].search([('partner_id', '=', main_partner.id)], limit=1) + if form_merchant: + form_merchant.write(filtered_params) + else: + lead = request.env['user.form.merchant'].create(filtered_params) + lead.partner_id = main_partner.id if not params['valid']: return self.response(code=400, description=params) - - main_partner = partner.get_main_parent() - - user_merchant_request = request.env['user.merchant.request'].create({ - 'user_id': partner.id, - 'merchant_id': lead.id, - 'user_company_id': main_partner.id - }) + if merchant_request: + user_merchant_request = request.env['user.merchant.request'].create({ + 'user_id': partner.id, + 'merchant_id': lead.id, + 'user_company_id': main_partner.id + }) return self.response(True) diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index c8025aa9..07990f58 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -29,9 +29,28 @@ class UserFormMerchant(models.Model): phone = fields.Char(string='No. Telepon Perusahaan') mobile = fields.Char(string='No. Handphone') bisnis_type = fields.Char(string='Bisnis Type') - category_perusahaan = fields.Char(string='Kategory Perusahaan') + category_perusahaan = fields.Char(string='Kategori Perusahaan') description = fields.Text(string='Deskripsi') + + # imformasi Vendor harga_tayang = fields.Char(string='Harga Tayang (HET)') + category_produk = fields.Char(string='Kategori Produk') + merk_dagang = fields.Char(string='Merk Dagang') + is_pengajuan_tempo = fields.Boolean(string='Apakah anda memiliki Form Pengajuan Tempo?') + tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') + kredit_limit = fields.Char(string='Kredit Limit') + waktu_pengiriman = fields.Char(string='Waktu Pengiriman') + terhitung_sejak = fields.Char(string='Terhitung Sejak') + + + + # syarat dagang + is_kembali_barang = fields.Char(string='Syarat Pengembalian Barang') # tulis tidak bisa pengembalian + + + + + file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") -- cgit v1.2.3 From 932fd84a5eb71d62f2a138f3ddb9ab9c0ccb0385 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 15 Jan 2025 13:20:21 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 76 ++++++++++- indoteknik_api/models/__init__.py | 1 + indoteknik_api/models/res_partner.py | 180 +++++++++++++++++++++++++ indoteknik_custom/models/user_form_merchant.py | 18 ++- 4 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 indoteknik_api/models/res_partner.py diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index 5749f5e5..e8153fbf 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -28,10 +28,10 @@ class Lead(controller.Controller): return self.response(True) - @http.route('/api/v1/merchant', auth='public', methods=['POST', 'OPTIONS'], csrf=False) + @http.route('/api/v1/merchant/', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def create_merchant(self, **kw): - merchant_request = True if kw.get('merchant_request') == 'true' else False + merchant_request = True if kw.get('merchantRequest') == 'true' else False params = self.get_request_params(kw, { # informasi perusahaan "name_merchant": [], @@ -54,10 +54,28 @@ class Lead(controller.Controller): "mobile": [], "bisnis_type": [], "category_perusahaan": [], + "website": [], "description": [], # informasi vendor "harga_tayang": [], + "merk_dagang": [], + "tempo_duration": [], + "kredit_limit": [], + "is_pengajuan_tempo": [], + "waktu_pengiriman": [], + "terhitung_sejak": [], + + # Syarat Perdagangan + "is_kembali_barang": [], + "sertifikat_produk": [], + "custom_sertifikat_produk": [], + "is_order_quantity": [], + "explain_garansi": [], + + + + "file_dokumenKtpDirut ": [], "file_kartuNama": [], "file_npwp": [], @@ -67,10 +85,42 @@ class Lead(controller.Controller): "file_dataProduk": [], "file_pricelist": [], }) - partner_id = int(kw.get('partner_id')) + partner_id = int(kw.get('id')) partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) main_partner = partner.get_main_parent() + if params['value']['is_pengajuan_tempo']: + if params['value']['is_pengajuan_tempo'] == 'ada': + params['value']['is_pengajuan_tempo'] = True + else: + params['value']['is_pengajuan_tempo'] = False + + if params['value']['is_kembali_barang']: + if params['value']['is_kembali_barang'] == 'ya': + params['value']['is_kembali_barang'] = kw.get('textReturn') + else: + params['value']['is_kembali_barang'] = 'Tidak dapat return' + + if kw.get('tenggat_waktu'): + if kw.get('tenggat_waktu') != 'custom': + params['value']['tenggat_waktu'] = kw.get('tenggat_waktu') + ' hari sejak data dikirimkan' + else: + params['value']['tenggat_waktu'] = kw.get('customTenggatWaktu') + + if kw.get('tempo_garansi'): + if kw.get('tempo_garansi') == '1': + params['value']['tempo_garansi'] = '6 Bulan Garansi' + elif kw.get('tempoTaransi') == '2': + params['value']['tempo_garansi'] = '1 Tahun Garansi' + else: + params['value']['tempo_garansi'] = '2 Tahun Garansi' + + if params['value']['is_order_quantity']: + if params['value']['is_order_quantity'] == 'ya': + params['value']['is_order_quantity'] = kw.get('minimumPembelian') + else: + params['value']['is_order_quantity'] = 'Tidak ada minimum order quantity' + filtered_params = {key: value for key, value in params['value'].items() if value} form_merchant = request.env['user.form.merchant'].search([('partner_id', '=', main_partner.id)], limit=1) if form_merchant: @@ -78,7 +128,11 @@ class Lead(controller.Controller): else: lead = request.env['user.form.merchant'].create(filtered_params) lead.partner_id = main_partner.id - + category_ids = '' + category_produk_ids = kw.get('categoryProduk', False) + if category_produk_ids: + category_ids = list(map(int, category_produk_ids.split(','))) + form_merchant.category_produk_ids = [(6, 0, category_ids)] if not params['valid']: return self.response(code=400, description=params) if merchant_request: @@ -89,3 +143,17 @@ class Lead(controller.Controller): }) return self.response(True) + + @http.route('/api/v1/detail-merchant/', auth='public', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def get_partner_detail_merchant(self, **kw): + params = self.get_request_params(kw, { + 'id': ['required', 'number'] + }) + partner = request.env['res.partner'].search([('id', '=', params['value']['id'])], limit=1) + main_partner = partner.get_main_parent() + form_merchant = request.env['user.form.merchant'].search([('partner_id', '=', main_partner.id)],limit=1) + if not form_merchant: + return self.response(code=404, description='form merchant not found') + form_merchant = request.env['res.partner'].api_single_response(form_merchant) + return self.response(form_merchant) 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/res_partner.py b/indoteknik_api/models/res_partner.py new file mode 100644 index 00000000..5da79884 --- /dev/null +++ b/indoteknik_api/models/res_partner.py @@ -0,0 +1,180 @@ +from odoo import models +import json +import base64 + +class ResPartner(models.Model): + _inherit = 'res.partner' + + def api_single_response(self, form_merchant, with_detail=''): + config = self.env['ir.config_parameter'] + + partner = form_merchant.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 form_merchant.dokumen_pengiriman : + # form_merchant_dokumen_pengiriman = form_merchant.dokumen_pengiriman + # mapping_dokumen = {item[0]: item[1] for item in dokumen_kirim} + # dokumen_pengiriman_list = [dokumen.strip() for dokumen in form_merchant_dokumen_pengiriman.split(',')] + # dokumen_pengiriman = [mapping_dokumen.get(dokumen, '4') for dokumen in dokumen_pengiriman_list] + data = { + 'name_merchant' : form_merchant.name_merchant, + 'pejabat_name' : form_merchant.pejabat_name, + 'pic_merchant' : form_merchant.pic_merchant, + 'pic_position' : form_merchant.pic_position, + 'address' : form_merchant.address, + 'state' : form_merchant.state.id, + 'city' : form_merchant.city.id, + 'district' : form_merchant.district.id, + 'subDistrict' : form_merchant.subDistrict.id, + 'zip' : form_merchant.zip, + 'bank_name' : form_merchant.bank_name, + 'rekening_name' : form_merchant.rekening_name, + 'account_number' : form_merchant.account_number, + 'email_company' : form_merchant.email_company, + 'email_sales' : form_merchant.email_sales, + 'email_finance' : form_merchant.email_finance, + 'phone' : form_merchant.phone, + 'mobile' : form_merchant.mobile, + 'bisnis_type' : form_merchant.bisnis_type, + 'category_perusahaan': form_merchant.category_perusahaan, + 'website' : form_merchant.website, + + # informasi Vendor + 'harga_tayang' : form_merchant.harga_tayang, + 'category_produk': ','.join([str(cat.id) for cat in form_merchant.category_produk_ids]) if form_merchant.category_produk_ids else '', + 'merk_dagang' : form_merchant.merk_dagang, + 'is_pengajuan_tempo' : 'ada' if form_merchant.is_pengajuan_tempo else 'tidak', + 'tempo_duration' : form_merchant.tempo_duration.id, + 'kredit_limit' : form_merchant.kredit_limit, + 'waktu_pengiriman' : form_merchant.waktu_pengiriman, + 'terhitung_sejak' : form_merchant.terhitung_sejak, + + + # syarat perdagangan + 'is_kembali_barang': 'ya' if form_merchant.is_kembali_barang else 'tidak', + 'text_return': form_merchant.is_kembali_barang, + 'tenggat_waktu': form_merchant.tenggat_waktu, + 'sertifikat_produk': form_merchant.sertifikat_produk, + 'custom_sertifikat_produk': '' if form_merchant.custom_sertifikat_produk == 'false' else form_merchant.custom_sertifikat_produk, + 'tempo_garansi': 1 if form_merchant.tempo_garansi == '6 Bulan Garansi' else 2 if form_merchant.tempo_garansi == '1 Tahun Garansi' else 3 , + 'explain_garansi': form_merchant.explain_garansi, + 'is_order_quantity': 'ya' if form_merchant.is_order_quantity else 'tidak', + 'minimum_pembelian': form_merchant.is_order_quantity, + # + # # Pengiriman + # 'PIC_tittle' : form_merchant.pic_tittle if form_merchant.pic_tittle else '', + # 'PIC_name' : form_merchant.pic_name if form_merchant.pic_name else '', + # 'street_pengiriman' : form_merchant.street_pengiriman if form_merchant.street_pengiriman else '', + # 'state_pengiriman' : form_merchant.state_id_pengiriman.id if form_merchant.state_id_pengiriman else '', + # 'city_pengiriman' : form_merchant.city_id_pengiriman.id if form_merchant.city_id_pengiriman else '', + # 'district_pengiriman': form_merchant.district_id_pengiriman.id if form_merchant.district_id_pengiriman else '', + # 'subDistrict_pengiriman': form_merchant.subDistrict_id_pengiriman.id if form_merchant.subDistrict_id_pengiriman else '', + # 'zip_pengiriman' : form_merchant.zip_pengiriman if form_merchant.zip_pengiriman else '', + # 'invoice_pic_tittle' : form_merchant.invoice_pic_tittle if form_merchant.invoice_pic_tittle else '', + # 'invoice_pic' : form_merchant.invoice_pic if form_merchant.invoice_pic else '', + # 'street_invoice' : form_merchant.street_invoice if form_merchant.street_invoice else '', + # 'state_invoice' : form_merchant.state_id_invoice.id if form_merchant.state_id_invoice else '', + # 'city_invoice' : form_merchant.city_id_invoice.id if form_merchant.city_id_invoice else '', + # 'district_invoice': form_merchant.district_id_invoice.id if form_merchant.district_id_invoice else '', + # 'subDistrict_invoice': form_merchant.subDistrict_id_invoice.id if form_merchant.subDistrict_id_invoice else '', + # 'zip_invoice': form_merchant.zip_invoice if form_merchant.zip_invoice else '', + # 'tukar_invoice_input' : form_merchant.tukar_invoice if form_merchant.tukar_invoice else '', + # 'tukar_invoice_input_pembayaran' : form_merchant.jadwal_bayar if form_merchant.jadwal_bayar else '', + # 'dokumen_pengiriman' : ','.join(dokumen_pengiriman) if dokumen_pengiriman else '', + # 'dokumen_pengiriman_input' : form_merchant.dokumen_pengiriman_input if form_merchant.dokumen_pengiriman_input else '', + # 'dokumen_kirim_input' : form_merchant.dokumen_kirim_input if form_merchant.dokumen_kirim_input else '', + # 'dokumen_pengiriman_invoice' : form_merchant.dokumen_invoice if form_merchant.dokumen_invoice else '', + # 'is_same_addrees': form_merchant.is_same_address if form_merchant.is_same_address else False, + # 'is_same_addrees_street': form_merchant.is_same_address_street if form_merchant.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 form_merchant.supplier_ids + # ] if form_merchant.supplier_ids else '', + # # Dokumen + # 'dokumen_npwp': + # { + # 'name': form_merchant.dokumen_npwp.name, + # 'base64': form_merchant.dokumen_npwp.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_npwp.mimetype, + # } if form_merchant.dokumen_npwp else '', + # 'dokumen_sppkp': { + # 'name': form_merchant.dokumen_sppkp.name, + # 'base64': form_merchant.dokumen_sppkp.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_sppkp.mimetype, + # } if form_merchant.dokumen_sppkp else '', + # 'dokumen_nib': + # { + # 'name': form_merchant.dokumen_nib.name, + # 'base64': form_merchant.dokumen_nib.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_nib.mimetype, + # }if form_merchant.dokumen_nib else '', + # 'dokumen_siup': + # { + # 'name': form_merchant.dokumen_siup.name, + # 'base64': form_merchant.dokumen_siup.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_siup.mimetype, + # }if form_merchant.dokumen_siup else '', + # 'dokumen_tdp': + # { + # 'name': form_merchant.dokumen_tdp.name, + # 'base64': form_merchant.dokumen_tdp.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_tdp.mimetype, + # }if form_merchant.dokumen_tdp else '', + # 'dokumen_skdp': + # { + # 'name': form_merchant.dokumen_skdp.name, + # 'base64': form_merchant.dokumen_skdp.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_skdp.mimetype, + # }if form_merchant.dokumen_skdp else '', + # 'dokumen_skt': + # { + # 'name': form_merchant.dokumen_skt.name, + # 'base64': form_merchant.dokumen_skt.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_skt.mimetype, + # }if form_merchant.dokumen_skt else '', + # 'dokumen_akta_perubahan': { + # 'name': form_merchant.dokumen_akta_perubahan.name, + # 'base64': form_merchant.dokumen_akta_perubahan.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_akta_perubahan.mimetype, + # } if form_merchant.dokumen_akta_perubahan else '', + # 'dokumen_ktp_dirut': { + # 'name': form_merchant.dokumen_ktp_dirut.name, + # 'base64': form_merchant.dokumen_ktp_dirut.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_ktp_dirut.mimetype, + # } if form_merchant.dokumen_ktp_dirut else '', + # 'dokumen_akta_pendirian': { + # 'name': form_merchant.dokumen_akta_pendirian.name, + # 'base64': form_merchant.dokumen_akta_pendirian.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_akta_pendirian.mimetype, + # } if form_merchant.dokumen_akta_pendirian else '', + # 'dokumen_laporan_keuangan': { + # 'name': form_merchant.dokumen_laporan_keuangan.name, + # 'base64': form_merchant.dokumen_laporan_keuangan.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_laporan_keuangan.mimetype, + # } if form_merchant.dokumen_laporan_keuangan else '', + # 'dokumen_foto_kantor': { + # 'name': form_merchant.dokumen_foto_kantor.name, + # 'base64': form_merchant.dokumen_foto_kantor.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_foto_kantor.mimetype, + # } if form_merchant.dokumen_foto_kantor else '', + # 'dokumen_tempat_bekerja': { + # 'name': form_merchant.dokumen_tempat_bekerja.name, + # 'base64': form_merchant.dokumen_tempat_bekerja.datas.decode('utf-8'), + # 'format': form_merchant.dokumen_tempat_bekerja.mimetype, + # } if form_merchant.dokumen_tempat_bekerja else '', + } + + return data \ No newline at end of file diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 07990f58..83ed2eeb 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -29,12 +29,20 @@ class UserFormMerchant(models.Model): phone = fields.Char(string='No. Telepon Perusahaan') mobile = fields.Char(string='No. Handphone') bisnis_type = fields.Char(string='Bisnis Type') + website = fields.Char(string='Website') category_perusahaan = fields.Char(string='Kategori Perusahaan') description = fields.Text(string='Deskripsi') # imformasi Vendor harga_tayang = fields.Char(string='Harga Tayang (HET)') - category_produk = fields.Char(string='Kategori Produk') + 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)] + + merk_dagang = fields.Char(string='Merk Dagang') is_pengajuan_tempo = fields.Boolean(string='Apakah anda memiliki Form Pengajuan Tempo?') tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') @@ -45,7 +53,13 @@ class UserFormMerchant(models.Model): # syarat dagang - is_kembali_barang = fields.Char(string='Syarat Pengembalian Barang') # tulis tidak bisa pengembalian + is_kembali_barang = fields.Char(string='Syarat Pengembalian Barang') + tenggat_waktu = fields.Char(string='Tenggat Waktu Perubahan Harga') + sertifikat_produk = fields.Char(string='Dokumen/Sertifikat yang Dimiliki Oleh Brand') + custom_sertifikat_produk = fields.Char(string='Dokumen/Sertifikat Lainnya') + tempo_garansi = fields.Char(string='Garansi') + explain_garansi = fields.Char(string='Garansi Yang Dimaksudkan') + is_order_quantity = fields.Char(string='Apakah Memiliki Minimum Order Quantity (MOQ)') -- cgit v1.2.3 From e3e7f29ad939a774878316e46e10a5c1370fda77 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 16 Jan 2025 15:53:36 +0700 Subject: sla --- indoteknik_custom/models/product_sla.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index dfdf7662..988aa78f 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -99,7 +99,7 @@ class ProductSla(models.Model): if len(leadtimes) > 0: avg_leadtime = sum(leadtimes) / len(leadtimes) rounded_leadtime = math.ceil(avg_leadtime) - estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration + estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 estimation_sla_days = estimation_sla / (24 * 60) self.sla = math.ceil(estimation_sla_days) self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file -- cgit v1.2.3 From c7f6d959e0e7a2e8adc45cff515f2b5666f3e732 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 20 Jan 2025 11:56:25 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 89 ++++++++++---- indoteknik_api/models/res_partner.py | 164 ++++++------------------- indoteknik_custom/models/user_form_merchant.py | 46 ++++--- indoteknik_custom/views/user_form_merchant.xml | 55 ++++++--- 4 files changed, 176 insertions(+), 178 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index e8153fbf..67856dad 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -1,6 +1,9 @@ from .. import controller from odoo import http from odoo.http import request +import base64 +import mimetypes +import json class Lead(controller.Controller): @http.route('/api/v1/lead', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @@ -70,20 +73,20 @@ class Lead(controller.Controller): "is_kembali_barang": [], "sertifikat_produk": [], "custom_sertifikat_produk": [], + "tempo_garansi": [], "is_order_quantity": [], "explain_garansi": [], - - - - "file_dokumenKtpDirut ": [], - "file_kartuNama": [], - "file_npwp": [], - "file_sppkp": [], - "file_suratPernyataan": [], - "file_fotoKantor": [], - "file_dataProduk": [], - "file_pricelist": [], + # # dokumen + # + # "file_npwp": [], + # "file_sppkp": [], + # "file_dokumenKtpDirut ": [], + # "file_kartuNama": [], + # "file_suratPernyataan": [], + # "file_fotoKantor": [], + # "file_dataProduk": [], + # "file_pricelist": [], }) partner_id = int(kw.get('id')) partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) @@ -105,22 +108,21 @@ class Lead(controller.Controller): if kw.get('tenggat_waktu') != 'custom': params['value']['tenggat_waktu'] = kw.get('tenggat_waktu') + ' hari sejak data dikirimkan' else: - params['value']['tenggat_waktu'] = kw.get('customTenggatWaktu') - - if kw.get('tempo_garansi'): - if kw.get('tempo_garansi') == '1': - params['value']['tempo_garansi'] = '6 Bulan Garansi' - elif kw.get('tempoTaransi') == '2': - params['value']['tempo_garansi'] = '1 Tahun Garansi' - else: - params['value']['tempo_garansi'] = '2 Tahun Garansi' + params['value']['tenggat_waktu'] = kw.get('custom_tenggat_waktu') if params['value']['is_order_quantity']: if params['value']['is_order_quantity'] == 'ya': - params['value']['is_order_quantity'] = kw.get('minimumPembelian') + params['value']['is_order_quantity'] = kw.get('minimum_pembelian') else: params['value']['is_order_quantity'] = 'Tidak ada minimum order quantity' + dokumen_kirim = [ + 'TKDN', + 'SNI', + 'Surat Terima Barang (STB)', + 'Lembar Penerimaan Barang (LPB)' + ] + filtered_params = {key: value for key, value in params['value'].items() if value} form_merchant = request.env['user.form.merchant'].search([('partner_id', '=', main_partner.id)], limit=1) if form_merchant: @@ -132,7 +134,50 @@ class Lead(controller.Controller): category_produk_ids = kw.get('categoryProduk', False) if category_produk_ids: category_ids = list(map(int, category_produk_ids.split(','))) - form_merchant.category_produk_ids = [(6, 0, category_ids)] + valid_category_ids = request.env['product.public.category'].search([('id', 'in', category_ids)]).ids + form_merchant.category_produk_ids = [(6, 0, valid_category_ids)] + + file_dokumen = kw.get('file_dokumen', False) + if file_dokumen: + form_dokumen = json.loads(file_dokumen) + + for dokumen in form_dokumen: + if form_dokumen[dokumen]['details']: + mimetype, _ = mimetypes.guess_type(form_dokumen[dokumen]['details']['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(form_dokumen[dokumen]['details']['format']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': form_dokumen[dokumen]['details']['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.form.merchant', + 'res_id': form_merchant.id, + 'mimetype': mimetype + }) + + if dokumen == 'file_npwp': + form_merchant.file_npwp = sppkp_attachment.id + + elif dokumen == 'file_sppkp': + form_merchant.file_sppkp = sppkp_attachment.id + + elif dokumen == 'file_dokumenKtpDirut': + form_merchant.file_dokumenKtpDirut = sppkp_attachment.id + + elif dokumen == 'file_kartuNama': + form_merchant.file_kartuNama = sppkp_attachment.id + + elif dokumen == 'file_suratPernyataan': + form_merchant.file_suratPernyataan = sppkp_attachment.id + + elif dokumen == 'file_fotoKantor': + form_merchant.file_fotoKantor = sppkp_attachment.id + + elif dokumen == 'file_dataProduk': + form_merchant.file_dataProduk = sppkp_attachment.id + + elif dokumen == 'file_pricelist': + form_merchant.file_pricelist = sppkp_attachment.id if not params['valid']: return self.response(code=400, description=params) if merchant_request: diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py index 5da79884..a8ba785d 100644 --- a/indoteknik_api/models/res_partner.py +++ b/indoteknik_api/models/res_partner.py @@ -6,22 +6,6 @@ class ResPartner(models.Model): _inherit = 'res.partner' def api_single_response(self, form_merchant, with_detail=''): - config = self.env['ir.config_parameter'] - - partner = form_merchant.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 form_merchant.dokumen_pengiriman : - # form_merchant_dokumen_pengiriman = form_merchant.dokumen_pengiriman - # mapping_dokumen = {item[0]: item[1] for item in dokumen_kirim} - # dokumen_pengiriman_list = [dokumen.strip() for dokumen in form_merchant_dokumen_pengiriman.split(',')] - # dokumen_pengiriman = [mapping_dokumen.get(dokumen, '4') for dokumen in dokumen_pengiriman_list] data = { 'name_merchant' : form_merchant.name_merchant, 'pejabat_name' : form_merchant.pejabat_name, @@ -62,119 +46,45 @@ class ResPartner(models.Model): 'tenggat_waktu': form_merchant.tenggat_waktu, 'sertifikat_produk': form_merchant.sertifikat_produk, 'custom_sertifikat_produk': '' if form_merchant.custom_sertifikat_produk == 'false' else form_merchant.custom_sertifikat_produk, - 'tempo_garansi': 1 if form_merchant.tempo_garansi == '6 Bulan Garansi' else 2 if form_merchant.tempo_garansi == '1 Tahun Garansi' else 3 , + 'tempo_garansi': form_merchant.tempo_garansi, 'explain_garansi': form_merchant.explain_garansi, - 'is_order_quantity': 'ya' if form_merchant.is_order_quantity else 'tidak', + 'is_order_quantity': 'ya' if form_merchant.is_order_quantity != 'Tidak ada minimum order quantity' else 'tidak', 'minimum_pembelian': form_merchant.is_order_quantity, - # - # # Pengiriman - # 'PIC_tittle' : form_merchant.pic_tittle if form_merchant.pic_tittle else '', - # 'PIC_name' : form_merchant.pic_name if form_merchant.pic_name else '', - # 'street_pengiriman' : form_merchant.street_pengiriman if form_merchant.street_pengiriman else '', - # 'state_pengiriman' : form_merchant.state_id_pengiriman.id if form_merchant.state_id_pengiriman else '', - # 'city_pengiriman' : form_merchant.city_id_pengiriman.id if form_merchant.city_id_pengiriman else '', - # 'district_pengiriman': form_merchant.district_id_pengiriman.id if form_merchant.district_id_pengiriman else '', - # 'subDistrict_pengiriman': form_merchant.subDistrict_id_pengiriman.id if form_merchant.subDistrict_id_pengiriman else '', - # 'zip_pengiriman' : form_merchant.zip_pengiriman if form_merchant.zip_pengiriman else '', - # 'invoice_pic_tittle' : form_merchant.invoice_pic_tittle if form_merchant.invoice_pic_tittle else '', - # 'invoice_pic' : form_merchant.invoice_pic if form_merchant.invoice_pic else '', - # 'street_invoice' : form_merchant.street_invoice if form_merchant.street_invoice else '', - # 'state_invoice' : form_merchant.state_id_invoice.id if form_merchant.state_id_invoice else '', - # 'city_invoice' : form_merchant.city_id_invoice.id if form_merchant.city_id_invoice else '', - # 'district_invoice': form_merchant.district_id_invoice.id if form_merchant.district_id_invoice else '', - # 'subDistrict_invoice': form_merchant.subDistrict_id_invoice.id if form_merchant.subDistrict_id_invoice else '', - # 'zip_invoice': form_merchant.zip_invoice if form_merchant.zip_invoice else '', - # 'tukar_invoice_input' : form_merchant.tukar_invoice if form_merchant.tukar_invoice else '', - # 'tukar_invoice_input_pembayaran' : form_merchant.jadwal_bayar if form_merchant.jadwal_bayar else '', - # 'dokumen_pengiriman' : ','.join(dokumen_pengiriman) if dokumen_pengiriman else '', - # 'dokumen_pengiriman_input' : form_merchant.dokumen_pengiriman_input if form_merchant.dokumen_pengiriman_input else '', - # 'dokumen_kirim_input' : form_merchant.dokumen_kirim_input if form_merchant.dokumen_kirim_input else '', - # 'dokumen_pengiriman_invoice' : form_merchant.dokumen_invoice if form_merchant.dokumen_invoice else '', - # 'is_same_addrees': form_merchant.is_same_address if form_merchant.is_same_address else False, - # 'is_same_addrees_street': form_merchant.is_same_address_street if form_merchant.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 form_merchant.supplier_ids - # ] if form_merchant.supplier_ids else '', - # # Dokumen - # 'dokumen_npwp': - # { - # 'name': form_merchant.dokumen_npwp.name, - # 'base64': form_merchant.dokumen_npwp.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_npwp.mimetype, - # } if form_merchant.dokumen_npwp else '', - # 'dokumen_sppkp': { - # 'name': form_merchant.dokumen_sppkp.name, - # 'base64': form_merchant.dokumen_sppkp.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_sppkp.mimetype, - # } if form_merchant.dokumen_sppkp else '', - # 'dokumen_nib': - # { - # 'name': form_merchant.dokumen_nib.name, - # 'base64': form_merchant.dokumen_nib.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_nib.mimetype, - # }if form_merchant.dokumen_nib else '', - # 'dokumen_siup': - # { - # 'name': form_merchant.dokumen_siup.name, - # 'base64': form_merchant.dokumen_siup.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_siup.mimetype, - # }if form_merchant.dokumen_siup else '', - # 'dokumen_tdp': - # { - # 'name': form_merchant.dokumen_tdp.name, - # 'base64': form_merchant.dokumen_tdp.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_tdp.mimetype, - # }if form_merchant.dokumen_tdp else '', - # 'dokumen_skdp': - # { - # 'name': form_merchant.dokumen_skdp.name, - # 'base64': form_merchant.dokumen_skdp.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_skdp.mimetype, - # }if form_merchant.dokumen_skdp else '', - # 'dokumen_skt': - # { - # 'name': form_merchant.dokumen_skt.name, - # 'base64': form_merchant.dokumen_skt.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_skt.mimetype, - # }if form_merchant.dokumen_skt else '', - # 'dokumen_akta_perubahan': { - # 'name': form_merchant.dokumen_akta_perubahan.name, - # 'base64': form_merchant.dokumen_akta_perubahan.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_akta_perubahan.mimetype, - # } if form_merchant.dokumen_akta_perubahan else '', - # 'dokumen_ktp_dirut': { - # 'name': form_merchant.dokumen_ktp_dirut.name, - # 'base64': form_merchant.dokumen_ktp_dirut.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_ktp_dirut.mimetype, - # } if form_merchant.dokumen_ktp_dirut else '', - # 'dokumen_akta_pendirian': { - # 'name': form_merchant.dokumen_akta_pendirian.name, - # 'base64': form_merchant.dokumen_akta_pendirian.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_akta_pendirian.mimetype, - # } if form_merchant.dokumen_akta_pendirian else '', - # 'dokumen_laporan_keuangan': { - # 'name': form_merchant.dokumen_laporan_keuangan.name, - # 'base64': form_merchant.dokumen_laporan_keuangan.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_laporan_keuangan.mimetype, - # } if form_merchant.dokumen_laporan_keuangan else '', - # 'dokumen_foto_kantor': { - # 'name': form_merchant.dokumen_foto_kantor.name, - # 'base64': form_merchant.dokumen_foto_kantor.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_foto_kantor.mimetype, - # } if form_merchant.dokumen_foto_kantor else '', - # 'dokumen_tempat_bekerja': { - # 'name': form_merchant.dokumen_tempat_bekerja.name, - # 'base64': form_merchant.dokumen_tempat_bekerja.datas.decode('utf-8'), - # 'format': form_merchant.dokumen_tempat_bekerja.mimetype, - # } if form_merchant.dokumen_tempat_bekerja else '', + + #dokumen + 'file_npwp': + { + 'name': form_merchant.file_npwp.name, + } if form_merchant.file_npwp else '', + 'file_sppkp': { + 'name': form_merchant.file_sppkp.name, + } if form_merchant.file_sppkp else '', + 'file_dokumenKtpDirut': + { + 'name': form_merchant.file_dokumenKtpDirut.name, + }if form_merchant.file_dokumenKtpDirut else '', + 'file_kartuNama': + { + 'name': form_merchant.file_kartuNama.name, + }if form_merchant.file_kartuNama else '', + 'file_suratPernyataan': + { + 'name': form_merchant.file_suratPernyataan.name, + }if form_merchant.file_suratPernyataan else '', + 'file_fotoKantor': + { + 'name': form_merchant.file_fotoKantor.name + }if form_merchant.file_fotoKantor else '', + 'file_dataProduk': + { + 'name': form_merchant.file_dataProduk.name, + }if form_merchant.file_dataProduk else '', + 'file_pricelist': { + 'name': form_merchant.file_pricelist.name, + } if form_merchant.file_pricelist else '', + + + } return data \ No newline at end of file diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 83ed2eeb..dd143381 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -28,9 +28,19 @@ class UserFormMerchant(models.Model): email_finance = fields.Char(string='Email Finance') phone = fields.Char(string='No. Telepon Perusahaan') mobile = fields.Char(string='No. Handphone') - bisnis_type = fields.Char(string='Bisnis Type') + bisnis_type = fields.Selection([ + ('1', 'PT'), + ('2', 'CV'), + ('3', 'Perorangan'), + ]) website = fields.Char(string='Website') - category_perusahaan = fields.Char(string='Kategori Perusahaan') + category_perusahaan = fields.Selection([ + ('1', 'Principal (Pemegang merk/Produsen)'), + ('2', 'Sole Distributor (Distributor Tunggal)'), + ('3', 'Authorized Distributor (Distributor Resmi)'), + ('4', 'Importer (Pengimpor Barang)'), + ('5', 'Wholesaler (Pedagang Besar)'), + ]) description = fields.Text(string='Deskripsi') # imformasi Vendor @@ -48,7 +58,11 @@ class UserFormMerchant(models.Model): tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') kredit_limit = fields.Char(string='Kredit Limit') waktu_pengiriman = fields.Char(string='Waktu Pengiriman') - terhitung_sejak = fields.Char(string='Terhitung Sejak') + terhitung_sejak = fields.Selection([ + ('1', 'Terima PO'), + ('2', 'Barang Dikirimkan'), + ('3', 'Tukar Faktur'), + ]) @@ -57,22 +71,24 @@ class UserFormMerchant(models.Model): tenggat_waktu = fields.Char(string='Tenggat Waktu Perubahan Harga') sertifikat_produk = fields.Char(string='Dokumen/Sertifikat yang Dimiliki Oleh Brand') custom_sertifikat_produk = fields.Char(string='Dokumen/Sertifikat Lainnya') - tempo_garansi = fields.Char(string='Garansi') + tempo_garansi = fields.Selection([ + ('1', '6 Bulan Garansi'), + ('2', '1 Tahun Garansi'), + ('3', '2 Tahun Garansi'), + ]) explain_garansi = fields.Char(string='Garansi Yang Dimaksudkan') is_order_quantity = fields.Char(string='Apakah Memiliki Minimum Order Quantity (MOQ)') - - - - file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") - file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") - file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") - file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") - file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, track_visibility="onchange") - file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, track_visibility="onchange") - file_dataProduk = fields.Binary(string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=True, track_visibility="onchange") - file_pricelist = fields.Binary(string="Pricelist", tracking=True, track_visibility="onchange") + # dokumen + file_npwp = fields.Many2one('ir.attachment', string="NPWP Perusahaan", tracking=3) + file_sppkp = fields.Many2one('ir.attachment', string="SPPKP Perusahaan", tracking=3) + file_dokumenKtpDirut = fields.Many2one('ir.attachment', string="KTP Dirut/Direktur", tracking=3) + file_kartuNama = fields.Many2one('ir.attachment', string="Kartu Nama", tracking=3) + file_suratPernyataan = fields.Many2one('ir.attachment', string="Surat Pernyataan Nomor Rekening", tracking=3) + file_fotoKantor = fields.Many2one('ir.attachment', string="Foto Gudang / Kantor Bagian Depan", tracking=3) + file_dataProduk = fields.Many2one('ir.attachment', string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=3) + file_pricelist = fields.Many2one('ir.attachment', string="Pricelist", tracking=3) @api.depends('name', 'name_merchant') def name_get(self): diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml index 230b6425..cbb7c756 100644 --- a/indoteknik_custom/views/user_form_merchant.xml +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -21,38 +21,65 @@
- + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - + -- cgit v1.2.3 From ff20b62d6932c6be4ffb56f63f3c05be3aa72c06 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 21 Jan 2025 13:43:29 +0700 Subject: update merchant --- indoteknik_api/controllers/api_v1/lead.py | 50 +++++++++++++++-------- indoteknik_api/models/res_partner.py | 17 ++++++-- indoteknik_custom/models/res_partner.py | 17 ++++++++ indoteknik_custom/models/user_form_merchant.py | 2 +- indoteknik_custom/models/user_merchant_request.py | 1 + indoteknik_custom/views/user_form_merchant.xml | 4 +- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index 67856dad..7ff8a8e9 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -34,7 +34,7 @@ class Lead(controller.Controller): @http.route('/api/v1/merchant/', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def create_merchant(self, **kw): - merchant_request = True if kw.get('merchantRequest') == 'true' else False + merchant_request = True if kw.get('merchant_request') == 'true' else False params = self.get_request_params(kw, { # informasi perusahaan "name_merchant": [], @@ -42,10 +42,10 @@ class Lead(controller.Controller): "pic_merchant": [], "pic_position": [], "address": [], - "state": [], - "city": [], - "district": [], - "subDistrict": [], + "state": ['number'], + "city": ['number'], + "district": ['number'], + "subDistrict": ['number'], "zip": [], "bank_name": [], "rekening_name": [], @@ -71,11 +71,10 @@ class Lead(controller.Controller): # Syarat Perdagangan "is_kembali_barang": [], - "sertifikat_produk": [], - "custom_sertifikat_produk": [], "tempo_garansi": [], "is_order_quantity": [], "explain_garansi": [], + "custom_sertifikat_produk": [], # # dokumen # @@ -102,7 +101,7 @@ class Lead(controller.Controller): if params['value']['is_kembali_barang'] == 'ya': params['value']['is_kembali_barang'] = kw.get('textReturn') else: - params['value']['is_kembali_barang'] = 'Tidak dapat return' + params['value']['is_kembali_barang'] = 'Tidak dapat direturn' if kw.get('tenggat_waktu'): if kw.get('tenggat_waktu') != 'custom': @@ -116,20 +115,37 @@ class Lead(controller.Controller): else: params['value']['is_order_quantity'] = 'Tidak ada minimum order quantity' - dokumen_kirim = [ - 'TKDN', - 'SNI', - 'Surat Terima Barang (STB)', - 'Lembar Penerimaan Barang (LPB)' - ] - - filtered_params = {key: value for key, value in params['value'].items() if value} + filtered_params = {key: value for key, value in params['value'].items() if kw.get(key) is not None} form_merchant = request.env['user.form.merchant'].search([('partner_id', '=', main_partner.id)], limit=1) + lead = [] if form_merchant: form_merchant.write(filtered_params) else: lead = request.env['user.form.merchant'].create(filtered_params) lead.partner_id = main_partner.id + + sertifikat = [ + 'TKDN', + 'SNI', + 'K3L', + ] + sertifikat_ids = kw.get('sertifikat_produk') + sertifikat_input = kw.get('custom_sertifikat_produk') + dokumen_sertifikat = [] + + if sertifikat_ids: + dokumen_kirim_ids = list(map(int, sertifikat_ids.split(','))) + dokumen_sertifikat = [sertifikat[i] for i in dokumen_kirim_ids if 0 <= i < len(sertifikat)] + if sertifikat_input != 'false' and sertifikat_input: + input_items = [item.strip() for item in sertifikat_input.split(',')] + dokumen_sertifikat.extend(item for item in input_items if item and item not in dokumen_sertifikat) + form_merchant.sertifikat_produk = sertifikat_input + else: + # Jika sertifikat_input kosong, hapus elemen custom (yang tidak ada di daftar `sertifikat`) + dokumen_sertifikat = [item for item in dokumen_sertifikat if item in sertifikat] + if dokumen_sertifikat: + form_merchant.sertifikat_produk = ', '.join(dokumen_sertifikat) + category_ids = '' category_produk_ids = kw.get('categoryProduk', False) if category_produk_ids: @@ -183,7 +199,7 @@ class Lead(controller.Controller): if merchant_request: user_merchant_request = request.env['user.merchant.request'].create({ 'user_id': partner.id, - 'merchant_id': lead.id, + 'merchant_id': form_merchant.id, 'user_company_id': main_partner.id }) diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py index a8ba785d..c313ffee 100644 --- a/indoteknik_api/models/res_partner.py +++ b/indoteknik_api/models/res_partner.py @@ -6,6 +6,17 @@ class ResPartner(models.Model): _inherit = 'res.partner' def api_single_response(self, form_merchant, with_detail=''): + sertifikat = [ + ['TKDN', '0'], + ['SNI', '1'], + ['K3L', '2'], + ] + dokumen_sertifikat = [] + if form_merchant.sertifikat_produk: + form_merchant_dokumen_sertifikat = form_merchant.sertifikat_produk + mapping_dokumen = {item[0]: item[1] for item in sertifikat} + dokumen_pengiriman_list = [dokumen.strip() for dokumen in form_merchant_dokumen_sertifikat.split(',')] + dokumen_sertifikat = [mapping_dokumen.get(dokumen, '3') for dokumen in dokumen_pengiriman_list] data = { 'name_merchant' : form_merchant.name_merchant, 'pejabat_name' : form_merchant.pejabat_name, @@ -41,10 +52,10 @@ class ResPartner(models.Model): # syarat perdagangan - 'is_kembali_barang': 'ya' if form_merchant.is_kembali_barang else 'tidak', - 'text_return': form_merchant.is_kembali_barang, + 'is_kembali_barang': 'tidak' if form_merchant.is_kembali_barang == 'Tidak dapat direturn' else 'ya', + 'text_return': form_merchant.is_kembali_barang if form_merchant.is_kembali_barang != 'Tidak dapat direturn' else '', 'tenggat_waktu': form_merchant.tenggat_waktu, - 'sertifikat_produk': form_merchant.sertifikat_produk, + 'sertifikat_produk': ','.join(dokumen_sertifikat) if dokumen_sertifikat else '', 'custom_sertifikat_produk': '' if form_merchant.custom_sertifikat_produk == 'false' else form_merchant.custom_sertifikat_produk, 'tempo_garansi': form_merchant.tempo_garansi, 'explain_garansi': form_merchant.explain_garansi, diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 9e53dae1..d7bd260f 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -76,8 +76,11 @@ class ResPartner(models.Model): "this feature", tracking=3) # MERCHANT + # informasi perusahaan name_merchant = fields.Char(string='Name') + pejabat_name = fields.Char(string='Pejabat Name') pic_merchant = fields.Char(string='PIC Merchant', required=True) + pic_position = fields.Char(string='Jabatan PIC') address_merchant = fields.Char(string='Alamat') state_merchant = fields.Many2one('res.country.state', string='State') city_merchant = fields.Many2one('vit.kota', string='Kota') @@ -92,6 +95,20 @@ class ResPartner(models.Model): email_finance_merchant = fields.Char(string='Email Finance') phone_merchant = fields.Char(string='No. Telepon Perusahaan') mobile_merchant = fields.Char(string='No. Handphone') + bisnis_type = fields.Selection([ + ('1', 'PT'), + ('2', 'CV'), + ('3', 'Perorangan'), + ]) + website_merchant = fields.Char(string='Website') + category_perusahaan = fields.Selection([ + ('1', 'Principal (Pemegang merk/Produsen)'), + ('2', 'Sole Distributor (Distributor Tunggal)'), + ('3', 'Authorized Distributor (Distributor Resmi)'), + ('4', 'Importer (Pengimpor Barang)'), + ('5', 'Wholesaler (Pedagang Besar)'), + ]) + harga_tayang = fields.Char(string='Harga Tayang (HET)') file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index dd143381..6d881621 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -8,7 +8,7 @@ class UserFormMerchant(models.Model): _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') - # informasi peruhaan + # informasi peruhsaan name_merchant = fields.Char(string='Name') pejabat_name = fields.Char(string='Pejabat Name') pic_merchant = fields.Char(string='PIC Merchant') diff --git a/indoteknik_custom/models/user_merchant_request.py b/indoteknik_custom/models/user_merchant_request.py index 7f6c5f3c..a373a771 100644 --- a/indoteknik_custom/models/user_merchant_request.py +++ b/indoteknik_custom/models/user_merchant_request.py @@ -70,6 +70,7 @@ class UserMerchantRequest(models.Model): def write(self, vals): is_approve = True if self.state_merchant == 'approved' or vals.get('state_merchant') == 'approved' else False if is_approve: + # Informasi Perusahaan self.user_company_id.name_merchant = self.merchant_id.name_merchant self.user_company_id.pic_merchant = self.merchant_id.pic_merchant self.user_company_id.address_merchant = self.merchant_id.address diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml index cbb7c756..cd3f1de3 100644 --- a/indoteknik_custom/views/user_form_merchant.xml +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -63,7 +63,7 @@ - + @@ -82,7 +82,7 @@ - + -- cgit v1.2.3 From 6809954d3029c98766d323eda40e6bd1fda8be0a Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 21 Jan 2025 16:34:18 +0700 Subject: update merchant contact --- indoteknik_custom/models/res_partner.py | 110 +++++++++++++++++++--- indoteknik_custom/models/user_form_merchant.py | 4 - indoteknik_custom/models/user_merchant_request.py | 28 +++++- indoteknik_custom/views/res_partner.xml | 2 +- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 8a420048..7852682f 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -182,20 +182,52 @@ class ResPartner(models.Model): ('4', 'Importer (Pengimpor Barang)'), ('5', 'Wholesaler (Pedagang Besar)'), ]) - + # Informasi Vendor harga_tayang = fields.Char(string='Harga Tayang (HET)') - file_dokumenKtpDirut = fields.Binary(string="KTP Dirut/Direktur", tracking=True, track_visibility="onchange") - file_kartuNama = fields.Binary(string="Kartu Nama", tracking=True, track_visibility="onchange") - file_npwp = fields.Binary(string="NPWP", tracking=True, track_visibility="onchange") - file_sppkp = fields.Binary(string="SPPKP", tracking=True, track_visibility="onchange") - file_suratPernyataan = fields.Binary(string="Surat Pernyataan Nomor Rekening", tracking=True, - track_visibility="onchange") - file_fotoKantor = fields.Binary(string="Foto Gudang / Kantor Bagian Depan", tracking=True, - track_visibility="onchange") - file_dataProduk = fields.Binary(string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=True, - track_visibility="onchange") - file_pricelist = fields.Binary(string="Pricelist", tracking=True, track_visibility="onchange") - description = fields.Text(string='Deskripsi') + category_produk_ids_merchant = fields.Many2many( + 'product.public.category', + string='Kategori Produk Merchant', + domain=lambda self: self._get_default_category_domain(), + relation='res_partner_category_produk_ids_merchant_rel' # Nama tabel relasi berbeda + ) + + @api.model + def _get_default_category_domain(self): + return [('parent_id', '=', False)] + + merk_dagang = fields.Char(string='Merk Dagang') + is_pengajuan_tempo = fields.Boolean(string='Apakah anda memiliki Form Pengajuan Tempo?') + tempo_duration_merchant = fields.Many2one('account.payment.term', string='Durasi Tempo') + kredit_limit = fields.Char(string='Kredit Limit') + waktu_pengiriman = fields.Char(string='Waktu Pengiriman') + terhitung_sejak = fields.Selection([ + ('1', 'Terima PO'), + ('2', 'Barang Dikirimkan'), + ('3', 'Tukar Faktur'), + ]) + + # syarat dagang + is_kembali_barang = fields.Char(string='Syarat Pengembalian Barang') + tenggat_waktu = fields.Char(string='Tenggat Waktu Perubahan Harga') + sertifikat_produk = fields.Char(string='Dokumen/Sertifikat yang Dimiliki Oleh Brand') + custom_sertifikat_produk = fields.Char(string='Dokumen/Sertifikat Lainnya') + tempo_garansi = fields.Selection([ + ('1', '6 Bulan Garansi'), + ('2', '1 Tahun Garansi'), + ('3', '2 Tahun Garansi'), + ]) + explain_garansi = fields.Char(string='Garansi Yang Dimaksudkan') + is_order_quantity = fields.Char(string='Apakah Memiliki Minimum Order Quantity (MOQ)') + + # dokumen + file_npwp = fields.Many2one('ir.attachment', string="NPWP Perusahaan", tracking=3) + file_sppkp = fields.Many2one('ir.attachment', string="SPPKP Perusahaan", tracking=3) + file_dokumenKtpDirut = fields.Many2one('ir.attachment', string="KTP Dirut/Direktur", tracking=3) + file_kartuNama = fields.Many2one('ir.attachment', string="Kartu Nama", tracking=3) + file_suratPernyataan = fields.Many2one('ir.attachment', string="Surat Pernyataan Nomor Rekening", tracking=3) + file_fotoKantor = fields.Many2one('ir.attachment', string="Foto Gudang / Kantor Bagian Depan", tracking=3) + file_dataProduk = fields.Many2one('ir.attachment', string="Data Produk (Item Name, Gambar, Deskripsi)", tracking=3) + file_pricelist = fields.Many2one('ir.attachment', string="Pricelist", tracking=3) @api.model def _default_payment_term(self): @@ -323,9 +355,12 @@ class ResPartner(models.Model): 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) - # Merchant + # MERCHANT + # Informasi Perusahaan vals['name_merchant'] = vals.get('name_merchant', self.name_merchant) + vals['pejabat_name'] = vals.get('pejabat_name', self.pejabat_name) vals['pic_merchant'] = vals.get('pic_merchant', self.pic_merchant) + vals['pic_position'] = vals.get('pic_position', self.pic_position) vals['address_merchant'] = vals.get('address_merchant', self.address_merchant) vals['state_merchant'] = vals.get('state_merchant', self.state_merchant) vals['city_merchant'] = vals.get('city_merchant', self.city_merchant) @@ -340,7 +375,30 @@ class ResPartner(models.Model): vals['email_finance_merchant'] = vals.get('email_finance_merchant', self.email_finance_merchant) vals['phone_merchant'] = vals.get('phone_merchant', self.phone_merchant) vals['mobile_merchant'] = vals.get('mobile_merchant', self.mobile_merchant) + vals['bisnis_type'] = vals.get('bisnis_type', self.bisnis_type) + vals['website_merchant'] = vals.get('website_merchant', self.website_merchant) + vals['category_perusahaan'] = vals.get('category_perusahaan', self.category_perusahaan) + + # Informasi Vendor vals['harga_tayang'] = vals.get('harga_tayang', self.harga_tayang) + vals['category_produk_ids_merchant'] = vals.get('category_produk_ids_merchant', self.category_produk_ids_merchant) + vals['merk_dagang'] = vals.get('merk_dagang', self.merk_dagang) + vals['is_pengajuan_tempo'] = vals.get('is_pengajuan_tempo', self.is_pengajuan_tempo) + vals['tempo_duration_merchant'] = vals.get('tempo_duration_merchant', self.tempo_duration_merchant) + vals['kredit_limit'] = vals.get('kredit_limit', self.kredit_limit) + vals['waktu_pengiriman'] = vals.get('waktu_pengiriman', self.waktu_pengiriman) + vals['terhitung_sejak'] = vals.get('terhitung_sejak', self.terhitung_sejak) + + # Syarat Dagang + vals['is_kembali_barang'] = vals.get('is_kembali_barang', self.is_kembali_barang) + vals['tenggat_waktu'] = vals.get('tenggat_waktu', self.tenggat_waktu) + vals['sertifikat_produk'] = vals.get('sertifikat_produk', self.sertifikat_produk) + vals['custom_sertifikat_produk'] = vals.get('custom_sertifikat_produk', self.custom_sertifikat_produk) + vals['tempo_garansi'] = vals.get('tempo_garansi', self.tempo_garansi) + vals['explain_garansi'] = vals.get('explain_garansi', self.explain_garansi) + vals['is_order_quantity'] = vals.get('is_order_quantity', self.is_order_quantity) + + # Dokumen vals['file_dokumenKtpDirut'] = vals.get('file_dokumenKtpDirut', self.file_dokumenKtpDirut) vals['file_kartuNama'] = vals.get('file_kartuNama', self.file_kartuNama) vals['file_npwp'] = vals.get('file_npwp', self.file_npwp) @@ -423,6 +481,9 @@ class ResPartner(models.Model): # Merchant 'name_merchant': vals.get('name_merchant'), + 'pejabat_name': vals.get('pejabat_name'), + 'pic_merchant': vals.get('pic_merchant'), + 'pic_position': vals.get('pic_position'), 'address_merchant': vals.get('address_merchant'), 'state_merchant': vals.get('state_merchant'), 'city_merchant': vals.get('city_merchant'), @@ -437,12 +498,33 @@ class ResPartner(models.Model): 'email_finance_merchant': vals.get('email_finance_merchant'), 'phone_merchant': vals.get('phone_merchant'), 'mobile_merchant': vals.get('mobile_merchant'), + 'bisnis_type': vals.get('bisnis_type'), + 'website_merchant': vals.get('website_merchant'), + 'category_perusahaan': vals.get('category_perusahaan'), + 'harga_tayang': vals.get('harga_tayang'), + 'category_produk_ids_merchant': vals.get('category_produk_ids_merchant'), + 'merk_dagang': vals.get('merk_dagang'), + 'is_pengajuan_tempo': vals.get('is_pengajuan_tempo'), + 'tempo_duration_merchant': vals.get('tempo_duration_merchant'), + 'kredit_limit': vals.get('kredit_limit'), + 'waktu_pengiriman': vals.get('waktu_pengiriman'), + 'terhitung_sejak': vals.get('terhitung_sejak'), + 'is_kembali_barang': vals.get('is_kembali_barang'), + 'tenggat_waktu': vals.get('tenggat_waktu'), + 'sertifikat_produk': vals.get('sertifikat_produk'), + 'custom_sertifikat_produk': vals.get('custom_sertifikat_produk'), + 'tempo_garansi': vals.get('tempo_garansi'), + 'explain_garansi': vals.get('explain_garansi'), + 'is_order_quantity': vals.get('is_order_quantity'), + 'file_dokumenKtpDirut': vals.get('file_dokumenKtpDirut'), 'file_kartuNama': vals.get('file_kartuNama'), 'file_npwp': vals.get('file_npwp'), 'file_sppkp': vals.get('file_sppkp'), 'file_suratPernyataan': vals.get('file_suratPernyataan'), 'file_fotoKantor': vals.get('file_fotoKantor'), + 'file_dataProduk': vals.get('file_dataProduk'), + 'file_pricelist': vals.get('file_pricelist'), 'description': vals.get('description'), } diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py index 6d881621..a804e93f 100644 --- a/indoteknik_custom/models/user_form_merchant.py +++ b/indoteknik_custom/models/user_form_merchant.py @@ -52,7 +52,6 @@ class UserFormMerchant(models.Model): def _get_default_category_domain(self): return [('parent_id', '=', False)] - merk_dagang = fields.Char(string='Merk Dagang') is_pengajuan_tempo = fields.Boolean(string='Apakah anda memiliki Form Pengajuan Tempo?') tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') @@ -64,8 +63,6 @@ class UserFormMerchant(models.Model): ('3', 'Tukar Faktur'), ]) - - # syarat dagang is_kembali_barang = fields.Char(string='Syarat Pengembalian Barang') tenggat_waktu = fields.Char(string='Tenggat Waktu Perubahan Harga') @@ -79,7 +76,6 @@ class UserFormMerchant(models.Model): explain_garansi = fields.Char(string='Garansi Yang Dimaksudkan') is_order_quantity = fields.Char(string='Apakah Memiliki Minimum Order Quantity (MOQ)') - # dokumen file_npwp = fields.Many2one('ir.attachment', string="NPWP Perusahaan", tracking=3) file_sppkp = fields.Many2one('ir.attachment', string="SPPKP Perusahaan", tracking=3) diff --git a/indoteknik_custom/models/user_merchant_request.py b/indoteknik_custom/models/user_merchant_request.py index a373a771..dd571cdc 100644 --- a/indoteknik_custom/models/user_merchant_request.py +++ b/indoteknik_custom/models/user_merchant_request.py @@ -72,7 +72,9 @@ class UserMerchantRequest(models.Model): if is_approve: # Informasi Perusahaan self.user_company_id.name_merchant = self.merchant_id.name_merchant + self.user_company_id.pejabat_name = self.merchant_id.pejabat_name self.user_company_id.pic_merchant = self.merchant_id.pic_merchant + self.user_company_id.pic_position = self.merchant_id.pic_position self.user_company_id.address_merchant = self.merchant_id.address self.user_company_id.state_merchant = self.merchant_id.state self.user_company_id.city_merchant = self.merchant_id.city @@ -87,11 +89,33 @@ class UserMerchantRequest(models.Model): self.user_company_id.email_finance_merchant = self.merchant_id.email_finance self.user_company_id.phone_merchant = self.merchant_id.phone self.user_company_id.mobile_merchant = self.merchant_id.mobile + self.user_company_id.bisnis_type = self.merchant_id.bisnis_type + self.user_company_id.website_merchant = self.merchant_id.website + self.user_company_id.category_perusahaan = self.merchant_id.category_perusahaan + + # Informasi Vendor self.user_company_id.harga_tayang = self.merchant_id.harga_tayang - self.user_company_id.file_dokumenKtpDirut = self.merchant_id.file_dokumenKtpDirut - self.user_company_id.file_kartuNama = self.merchant_id.file_kartuNama + self.user_company_id.category_produk_ids_merchant = self.merchant_id.category_produk_ids + self.user_company_id.merk_dagang = self.merchant_id.merk_dagang + self.user_company_id.is_pengajuan_tempo = self.merchant_id.is_pengajuan_tempo + self.user_company_id.tempo_duration_merchant = self.merchant_id.tempo_duration + self.user_company_id.kredit_limit = self.merchant_id.kredit_limit + self.user_company_id.waktu_pengiriman = self.merchant_id.waktu_pengiriman + self.user_company_id.terhitung_sejak = self.merchant_id.terhitung_sejak + + # Syarat Perdagangan + self.user_company_id.is_kembali_barang = self.merchant_id.is_kembali_barang + self.user_company_id.tenggat_waktu = self.merchant_id.tenggat_waktu + self.user_company_id.sertifikat_produk = self.merchant_id.sertifikat_produk + self.user_company_id.tempo_garansi = self.merchant_id.tempo_garansi + self.user_company_id.explain_garansi = self.merchant_id.explain_garansi + self.user_company_id.is_order_quantity = self.merchant_id.is_order_quantity + + # Dokumen self.user_company_id.file_npwp = self.merchant_id.file_npwp self.user_company_id.file_sppkp = self.merchant_id.file_sppkp + self.user_company_id.file_dokumenKtpDirut = self.merchant_id.file_dokumenKtpDirut + self.user_company_id.file_kartuNama = self.merchant_id.file_kartuNama self.user_company_id.file_suratPernyataan = self.merchant_id.file_suratPernyataan self.user_company_id.file_fotoKantor = self.merchant_id.file_fotoKantor self.user_company_id.file_dataProduk = self.merchant_id.file_dataProduk diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 863b8514..1f497f7a 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -224,7 +224,7 @@ - + -- cgit v1.2.3 From 992619cde1b7d19ce29db2cd314e8b62c8bec978 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 22 Jan 2025 13:16:27 +0700 Subject: update tampilan merchant --- indoteknik_custom/views/res_partner.xml | 46 ++++++++++++++++++-------- indoteknik_custom/views/user_form_merchant.xml | 14 ++++---- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 1f497f7a..19d4b940 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -190,40 +190,58 @@ - + + + - - + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - diff --git a/indoteknik_custom/views/user_form_merchant.xml b/indoteknik_custom/views/user_form_merchant.xml index cd3f1de3..ae5a0f9f 100644 --- a/indoteknik_custom/views/user_form_merchant.xml +++ b/indoteknik_custom/views/user_form_merchant.xml @@ -98,12 +98,12 @@ - + + + + + + + \ No newline at end of file -- cgit v1.2.3 From a571531bd8626f9bee25e89c62bbd9268ed30597 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 22 Jan 2025 16:32:48 +0700 Subject: add checj progres merchant --- indoteknik_api/controllers/api_v1/lead.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/lead.py b/indoteknik_api/controllers/api_v1/lead.py index f2793838..e81a2781 100644 --- a/indoteknik_api/controllers/api_v1/lead.py +++ b/indoteknik_api/controllers/api_v1/lead.py @@ -218,3 +218,17 @@ class Lead(controller.Controller): return self.response(code=404, description='form merchant not found') form_merchant = request.env['res.partner'].api_single_response_merchant(form_merchant) return self.response(form_merchant) + + @http.route('/api/v1/check-merchant/', auth='public', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def get_partner_form_merchant(self, **kw): + params = self.get_request_params(kw, { + 'id': ['required', 'number'] + }) + partner = request.env['res.partner'].search([('id', '=', params['value']['id'])], limit=1) + main_partner = partner.get_main_parent() + form_merchant = request.env['user.merchant.request'].search([('user_company_id', '=', main_partner.id)], limit=1) + if form_merchant: + return self.response(form_merchant.state_merchant) + else: + return self.response(code=404, description='form merchant not found') \ No newline at end of file -- cgit v1.2.3 From 2644e259339cd921babf49218aadbdcedb0c8937 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 28 Jan 2025 09:46:48 +0700 Subject: change product sla --- indoteknik_api/controllers/api_v1/product.py | 72 ++++++++--------- indoteknik_custom/models/product_sla.py | 114 +++++++++++++++------------ indoteknik_custom/views/product_sla.xml | 5 +- 3 files changed, 100 insertions(+), 91 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 7570015f..5d564ff9 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -34,31 +34,34 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - @http.route(prefix + 'product/variants/sla', auth='none', type='json', csrf=False, cors='*', methods=['GET', 'OPTIONS']) + @http.route(prefix + 'product/variants/sla', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() - def get_product_template_sla_by_id(self, **kw): - json_raw = json.loads(request.httprequest.data) + def get_product_template_sla_by_id(self, **kwargs): + body_params = kwargs.get('ids') - ids = json_raw.get('ids') - ids = list(map(int, ids)) + if not body_params: + return self.response('Failed', code=400, description='id is required') - if not ids or not isinstance(ids, list): - return ({'status' : 'Failed','message': 'Parameter "ids" harus berupa list dan tidak boleh kosong.'}) + ids = [int(id.strip()) for id in body_params.split(',') if id.strip().isdigit()] - sla_days = 0 + sla_duration = 0 + sla_unit = 'Hari' + include_instant = True products = request.env['product.product'].search([('id', 'in', ids)]) if len(products) < 1: - return ({ - 'status' : 'Failed', - 'message' : 'Produk Tidak Di Temukan.' - }) + return self.response( + 'Failed', + code=400, + description='Produk Tidak Di Temukan.' + ) product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) if len(product_slas) < 1: - return ({ - 'status' : 'Failed', - 'message' : 'Produk Tidak Di Temukan.' - }) + return self.response( + 'Failed', + code=400, + description='SLA Tidak Di Temukan.' + ) # Mapping SLA untuk mempermudah lookup sla_map = {sla.product_variant_id.id: sla for sla in product_slas} @@ -66,24 +69,20 @@ class Product(controller.Controller): for product in products: product_sla = sla_map.get(product.id) if product_sla: - sla_days = max(sla_days, product_sla.sla_vendor_id.duration) + sla_duration = max(sla_duration, int(product_sla.sla)) + sla_unit = product_sla.sla_vendor_id.unit if product.qty_free_bandengan < 1 : if product_sla.sla_vendor_id.unit != 'jam': - return ({ - 'status' : 'Success', - 'data' : [{ - 'include_instant': False, - 'sla_days': sla_days - }], - }) + include_instant = False + break + # Jika semua loop selesai tanpa include_instant menjadi False - return ({ - 'status' : 'Success', - 'data' : [{ - 'include_instant': True, - 'sla_days': sla_days - }], - }) + return self.response({ + 'include_instant': include_instant, + 'sla_duration': sla_duration, + 'sla_unit': sla_unit + } + ) @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() @@ -98,18 +97,12 @@ class Product(controller.Controller): ('product_variant_id', '=', id), ('write_date', '>=', date_7_days_ago.strftime("%Y-%m-%d %H:%M:%S")) ], limit=1) - - include_instant = False qty_available = product.qty_free_bandengan - if qty_available > 0 : - include_instant = True - else : + if qty_available < 1 : qty_available = 0 - if product_sla.sla_vendor_id.unit == 'jam': - include_instant = True qty = 0 sla_date = '-' @@ -150,8 +143,7 @@ class Product(controller.Controller): data = { 'qty': qty, - 'sla_date': sla_date, - 'can_instant': include_instant + 'sla_date': sla_date } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 988aa78f..04ad2ffd 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -14,51 +14,53 @@ class ProductSla(models.Model): product_variant_id = fields.Many2one('product.product',string='Product') sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') - avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) - leadtime = fields.Char(string='Leadtime', readonly=True) + sla_logistic = fields.Char(string='SLA Logistic', readonly=True) + sla_logistic_unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Logistic Time" + ) + sla_logistic_duration_unit = fields.Char(string="SLA Logistic Duration (Unit)") sla = fields.Char(string='SLA', readonly=True) version = fields.Integer(string="Version", compute="_compute_version") def _compute_version(self): for sla in self: sla.version = sla.product_variant_id.sla_version + + - def generate_product_variant_id_sla(self, limit=5000): - # Filter produk non-Altama - + def generate_product_variant_id_sla(self, limit=500): + offset = 0 + # while True: products = self.env['product.product'].search([ - ('x_manufacture', 'not in', [10, 122, 89]), - ('location_id', '=', 57), - ('stock_move_ids', '!=', False), - ], order='sla_version asc', limit=limit) + ('active', '=', True), + ('sale_ok', '=', True), + ], order='sla_version asc', limit=limit, offset=offset) + + # if not products: + # break - i = 1 for product in products: - _logger.info(f'Product SLA: {i}/{len(products)}') - i += 1 - product.sla_version += 1 + _logger.info(f'Memproses SLA untuk produk ID {product.id}, versi {product.sla_version}') product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) - print(product_sla.id, product.id) if not product_sla: - product_sla = self.env['product.sla'].create({ - 'product_variant_id': product.id, - }) - + product_sla = self.create({'product_variant_id': product.id}) + product_sla.generate_product_sla() + # Tandai produk sebagai sudah diproses + product.sla_version += 1 + + offset += limit + + def generate_product_sla(self): - self.avg_leadtime = '-' - self.sla = '-' + # self.sla_logistic = '-' + # self.sla_logistic_duration_unit = '-' + # self.sla = '-' product = self.product_variant_id - - # qty_available = 0 - # qty_available = product.qty_onhand_bandengan - - - # if qty_available > 0: - # self.sla = 1 q_vendor = [ ('product_id', '=', product.id), @@ -68,6 +70,8 @@ class ProductSla(models.Model): vendor = self.env['purchase.pricelist'].search(q_vendor) vendor_duration = 0 + + #SLA Vendor if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 @@ -78,28 +82,40 @@ class ProductSla(models.Model): vendor_duration = vendor_sla.duration * 60 self.sla_vendor_id = vendor_sla.id if vendor_sla else False + #SLA Logistik selalu 1 hari + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = math.ceil(estimation_sla_days) + self.sla_logistic = int(1) + self.sla_logistic_unit = 'hari' + self.sla_logistic_duration_unit = '1 hari' + else: + self.unlink() + else: + self.unlink() + - query = [ - ('product_id', '=', product.id), - ('picking_id', '!=', False), - ('picking_id.location_id', '=', 57), - ('picking_id.state', 'not in', ['cancel']) - ] - picking = self.env['stock.move.line'].search(query) + # query = [ + # ('product_id', '=', product.id), + # ('picking_id', '!=', False), + # ('picking_id.location_id', '=', 57), + # ('picking_id.state', 'not in', ['cancel']) + # ] + # picking = self.env['stock.move.line'].search(query) - leadtimes=[] - for stock in picking: - date_delivered = stock.picking_id.driver_departure_date - date_do_ready = stock.picking_id.date_reserved - if date_delivered and date_do_ready: - leadtime = date_delivered - date_do_ready - leadtime_in_days = leadtime.days - leadtimes.append(leadtime_in_days) + # leadtimes=[] + # for stock in picking: + # date_delivered = stock.picking_id.driver_departure_date + # date_do_ready = stock.picking_id.date_reserved + # if date_delivered and date_do_ready: + # leadtime = date_delivered - date_do_ready + # leadtime_in_days = leadtime.days + # leadtimes.append(leadtime_in_days) - if len(leadtimes) > 0: - avg_leadtime = sum(leadtimes) / len(leadtimes) - rounded_leadtime = math.ceil(avg_leadtime) - estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 - estimation_sla_days = estimation_sla / (24 * 60) - self.sla = math.ceil(estimation_sla_days) - self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file + # if len(leadtimes) > 0: + # avg_leadtime = sum(leadtimes) / len(leadtimes) + # rounded_leadtime = math.ceil(avg_leadtime) + # estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 + # estimation_sla_days = estimation_sla / (24 * 60) + # self.sla = math.ceil(estimation_sla_days) + # self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 3722ef3d..9179730f 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -8,7 +8,7 @@ - + @@ -23,7 +23,8 @@ - + + -- cgit v1.2.3 From 346b6dc89cbde3640413714175cdc438544a664c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 28 Jan 2025 14:58:05 +0700 Subject: save response --- indoteknik_custom/models/stock_picking.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cd330aeb..6967e1a3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -165,6 +165,11 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + # Biteship Section + biteship_id = fields.Char(string="Biteship Respon ID") + biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") + biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + def _compute_lalamove_image_html(self): for record in self: if record.lalamove_image_url: @@ -387,7 +392,7 @@ class StockPicking(models.Model): } # Cek jika pengiriman instant atau same_day - if "instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type: + if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ "origin_note": "BELAKANG INDOMARET", "courier_company": self.carrier_id.name.lower(), @@ -404,10 +409,19 @@ class StockPicking(models.Model): # Kirim request ke Biteship response = requests.post(url, headers=headers, json=payload) - if response.status_code == 201: - return response.json() + if response.status_code == 200: + data = response.json() + + self.biteship_id = data.get("id", "") + self.biteship_tracking_id = data.get("tracking_id", "") + self.biteship_waybill_id = data.get("waybill_id", "") + + return data else: - raise UserError(f"Error saat mengirim ke Biteship: {response.content}") + error_data = response.json() + error_message = error_data.get("error", "Unknown error") + error_code = error_data.get("code", "No code provided") + raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): -- cgit v1.2.3 From 74796f3390ad90e2ae981351cd0491aca6dccc9c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 31 Jan 2025 16:45:07 +0700 Subject: add ready to ship date --- indoteknik_custom/models/product_template.py | 13 +++++++ indoteknik_custom/models/sale_order.py | 57 +++++++++++++++++++++++++++- indoteknik_custom/models/stock_picking.py | 29 +++++++++++++- indoteknik_custom/views/product_product.xml | 8 ++++ indoteknik_custom/views/sale_order.xml | 1 + indoteknik_custom/views/stock_picking.xml | 5 ++- 6 files changed, 109 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 5bedae13..8c57774e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -403,6 +403,19 @@ class ProductProduct(models.Model): merchandise_ok = fields.Boolean(string='Product Promotion') qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + def generate_product_sla(self): + product_variant_ids = self.env.context.get('active_ids', []) + product_variant = self.search([('id', 'in', product_variant_ids)]) + sla_record = self.env['product.sla'].search([('product_variant_id', '=', product_variant.id)], limit=1) + + if sla_record: + sla_record.generate_product_sla() + else: + new_sla_record = self.env['product.sla'].create({ + 'product_variant_id': product_variant.id, + }) + new_sla_record.generate_product_sla() + def _compute_qr_code_variant(self): for rec in self: # Skip inactive variants diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 48195b77..32e6f11f 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -144,6 +144,16 @@ class SaleOrder(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) + estimated_ready_ship_date = fields.Datetime( + string='ET Ready to Ship', + copy=False, + store=True + ) + expected_ready_ship_date = fields.Datetime( + string='ET Ready to Ship FIX', + copy=False, + store=True + ) @api.onchange('payment_status') def _is_continue_transaction(self): @@ -377,6 +387,45 @@ class SaleOrder(models.Model): else: rec.eta_date = False + def _compute_etrts_date(self): + max_slatime = 100 + + # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke + max_leadtime = 0 + + for line in self.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)]) + slatime = int(product_sla.sla) or 1 + max_slatime = max(max_slatime, slatime) + + for rec in self: + if rec.date_order: + eta_date = datetime.now() + timedelta(days=max_slatime) + rec.estimated_ready_ship_date = eta_date + if not rec.expected_ready_ship_date: + rec.expected_ready_ship_date = eta_date + # else: + # rec.estimated_ready_ship_date = False + + def _set_etrts_date(self): + for order in self: + if order.state in ('done', 'cancel', 'sale'): + raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) + # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) + + @api.onchange('estimated_ready_ship_date') + def _onchange_estimated_ready_ship_date(self): + for record in self: + if (record.estimated_ready_ship_date and record.expected_ready_ship_date): + if(record.estimated_ready_ship_date < record.expected_ready_ship_date): + return { + 'warning': { + 'title': _('Requested date is too soon.'), + 'message': _("The delivery date is sooner than the expected date." + "You may be unable to honor the delivery date.") + } + } + def _prepare_invoice(self): """ Prepare the dict of values to create the new invoice for a sales order. This method may be @@ -573,7 +622,7 @@ class SaleOrder(models.Model): def write(self, vals): res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': @@ -1009,6 +1058,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() + order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() @@ -1383,13 +1433,14 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) + order._compute_etrts_date() # order._update_partner_details() return order def write(self, vals): # Call the super method to handle the write operation res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() # Check if the update is coming from a save operation # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): # self._update_partner_details() @@ -1424,4 +1475,6 @@ class SaleOrder(models.Model): raise UserError( "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") res = super(SaleOrder, self).write(vals) + if 'order_line' in vals: + self._compute_etrts_date() return res \ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6967e1a3..f766dc3f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ 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 timedelta, datetime +from datetime import timedelta, datetime as waktu from itertools import groupby import pytz, requests, json, requests from dateutil import parser @@ -169,6 +169,33 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', store=True, default=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') + + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_hours(self): + for record in self: + if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # Gunakan nilai yang sangat besar sebagai placeholder + record.countdown_hours = 999999 + else: + delta = record.estimated_ready_ship_date - waktu.now() + record.countdown_hours = delta.total_seconds() / 3600 + + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_ready_to_ship(self): + for record in self: + if record.state in ('cancel', 'done'): + record.countdown_ready_to_ship = False + else: + if record.estimated_ready_ship_date: + delta = record.estimated_ready_ship_date - waktu.now() + days = delta.days + hours, remainder = divmod(delta.seconds, 3600) + record.countdown_ready_to_ship = f"{days} days, {hours} hours" + else: + record.countdown_ready_to_ship = False def _compute_lalamove_image_html(self): for record in self: diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml index 71748e44..b214dc87 100644 --- a/indoteknik_custom/views/product_product.xml +++ b/indoteknik_custom/views/product_product.xml @@ -31,6 +31,14 @@ model.action_sync_to_solr() + + Generate Product SLA + + + code + model.generate_product_sla() + + Sync Variant To Solr: Solr Flag 2 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 008a04ed..09a71912 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -69,6 +69,7 @@ + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 8acba608..ae6ae940 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,7 +7,8 @@ - create_date desc + countdown_hours asc + @@ -18,6 +19,8 @@ + + -- cgit v1.2.3 From d0647f4b4a0df94c7b51852823df37eeb5b89e3e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 3 Feb 2025 14:45:18 +0700 Subject: add generate vendor sla --- indoteknik_custom/models/vendor_sla.py | 75 ++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 9af86a14..67b6ffc3 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -1,4 +1,7 @@ from odoo import models, fields, api +import logging +import math +_logger = logging.getLogger(__name__) class VendorSLA(models.Model): _name = 'vendor.sla' @@ -12,15 +15,81 @@ class VendorSLA(models.Model): string="SLA Time" ) duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") - - + + # pertama, lakukan group by vendor pada modul purchase.order + # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order + # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration + def generate_vendor_id_sla(self): + # Step 1: Group purchase orders by vendor (partner_id) + po_env = self.env['purchase.order'] + pos = po_env.read_group( + domain=[('state', '=', 'done')], + fields=['partner_id', 'date_planned', 'date_approve'], + groupby=['partner_id'], + lazy=False + ) + + for group in pos: + partner_id = group['partner_id'][0] + total_duration = 0 + count = 0 + + # Step 2: Calculate the average duration for each vendor + pos_for_vendor = po_env.search([ + ('partner_id', '=', partner_id), + ('state', '=', 'done'), + ('date_planned', '>=', '2023-01-01') + ]) + + for po in pos_for_vendor: + if po.date_planned and po.date_approve: + date_planned = fields.Datetime.to_datetime(po.date_planned) + date_approve = fields.Datetime.to_datetime(po.date_approve) + if date_planned < date_approve: continue + duration = (date_planned - date_approve).total_seconds() / 3600 # Convert to hours + total_duration += duration + count += 1 + + if count > 0: + average_duration = total_duration / count + + # Step 3: Update the duration field in the corresponding res.partner record + vendor_sla = self.search([('id_vendor', '=', partner_id)], limit=1) + + # Konversi jam ke hari jika diperlukan + if average_duration >= 24: + days = average_duration / 24 + if days - int(days) > 0.5: # Jika sisa lebih dari 0,5, bulatkan ke atas + days = int(days) + 1 + else: # Jika sisa 0,5 atau kurang, bulatkan ke bawah + days = int(days) + duration_to_save = days + unit_to_save = 'hari' + else: + duration_to_save = round(average_duration) + unit_to_save = 'jam' + + # Update atau create vendor SLA record + if vendor_sla: + vendor_sla.write({ + 'duration': duration_to_save, + 'unit': unit_to_save + }) + else: + self.create({ + 'id_vendor': partner_id, + 'duration': duration_to_save, + 'unit': unit_to_save + }) + _logger.info(f'Proses SLA untuk Vendor selesai dilakukan') + @api.depends('duration', 'unit') def _compute_duration_unit(self): for record in self: if record.duration and record.unit: record.duration_unit = f"{record.duration} {record.unit}" else: - record.duration_unit = "" + record.duration_unit = "-" \ No newline at end of file -- cgit v1.2.3 From 88834d1fcda0914867fc6420ba8cb9b4046b3a11 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Feb 2025 16:38:59 +0700 Subject: add public holiday --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/public_holiday.py | 11 +++++ indoteknik_custom/models/sale_order.py | 3 +- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/public_holiday.xml | 57 ++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 indoteknik_custom/models/public_holiday.py create mode 100644 indoteknik_custom/views/public_holiday.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index c1593c9e..580f43de 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -159,6 +159,7 @@ 'report/report_sale_order.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', + 'views/public_holiday.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 43fbf146..4c1ef68c 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -140,3 +140,4 @@ from . import va_multi_reject from . import vendor_sla from . import stock_immediate_transfer from . import coretax_fatur +from . import public_holiday diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py new file mode 100644 index 00000000..70b7c53a --- /dev/null +++ b/indoteknik_custom/models/public_holiday.py @@ -0,0 +1,11 @@ +from odoo import api, fields, models +from datetime import timedelta, datetime + +class PublicHoliday(models.Model): + _name = 'hr.public.holiday' + _description = 'Public Holidays' + + name = fields.Char(string='Holiday Name', required=True) + start_date = fields.Date('Start Holiday Date', required=True) + end_date = fields.Date('End Holiday Date', required=True) + # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 32e6f11f..7eb4151a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -387,8 +387,7 @@ class SaleOrder(models.Model): else: rec.eta_date = False - def _compute_etrts_date(self): - max_slatime = 100 + max_slatime = 0 # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke max_leadtime = 0 diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index b6212f1b..1f3835fa 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -157,3 +157,4 @@ access_User_pengajuan_tempo_line,access.user.pengajuan.tempo.line,model_user_pen 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 +access_hr_public_holiday,confirm.hr.public.holiday,model_hr_public_holiday,,1,1,1,0 diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml new file mode 100644 index 00000000..d0b9c5d5 --- /dev/null +++ b/indoteknik_custom/views/public_holiday.xml @@ -0,0 +1,57 @@ + + + + + + hr.public.holiday access + + + + + + + + + + + hr.public.holiday.form + hr.public.holiday + + + + + + + + + + + + + + + hr.public.holiday.tree + hr.public.holiday + + + + + + + + + + Public Holidays + hr.public.holiday + tree,form + + + + + -- cgit v1.2.3 From 0dbcd5eb060924a0860b2586776f65d5ce19b9ef Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 7 Feb 2025 15:07:01 +0700 Subject: estimation delerivery date --- indoteknik_api/controllers/api_v1/product.py | 18 ++++++- indoteknik_custom/models/sale_order.py | 77 +++++++++++++--------------- indoteknik_custom/views/sale_order.xml | 2 +- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 5d564ff9..93ef305c 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -9,6 +9,17 @@ import json _logger = logging.getLogger(__name__) +def get_days_until_next_business_day(start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + + while today.weekday() >= 5 : + today += timedelta(days=1) + offset += 1 + + return offset + + class Product(controller.Controller): prefix = '/api/v1/' @@ -75,12 +86,17 @@ class Product(controller.Controller): if product_sla.sla_vendor_id.unit != 'jam': include_instant = False break + + start_date = datetime.today().date() + additional_days = get_days_until_next_business_day(start_date) # Jika semua loop selesai tanpa include_instant menjadi False return self.response({ 'include_instant': include_instant, 'sla_duration': sla_duration, - 'sla_unit': sla_unit + 'sla_additional_days': additional_days, + 'sla_total' : int(sla_duration) + int(additional_days), + 'sla_unit': 'Hari' if additional_days > 0 else sla_unit } ) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 32e6f11f..cae99447 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -146,10 +146,11 @@ class SaleOrder(models.Model): ]) estimated_ready_ship_date = fields.Datetime( string='ET Ready to Ship', - copy=False, + compute='_compute_etrts_date', store=True + ) - expected_ready_ship_date = fields.Datetime( + expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship FIX', copy=False, store=True @@ -373,39 +374,29 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True + @api.depends('date_order', 'estimated_arrival_days', 'state') def _compute_eta_date(self): - max_leadtime = 0 - - for line in self.order_line: - leadtime = line.vendor_id.leadtime - max_leadtime = max(max_leadtime, leadtime) - - for rec in self: - if rec.date_order and rec.state not in ['cancel', 'draft']: - eta_date = datetime.now() + timedelta(days=max_leadtime) - rec.eta_date = eta_date + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) else: rec.eta_date = False - def _compute_etrts_date(self): - max_slatime = 100 - - # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke - max_leadtime = 0 - - for line in self.order_line: - product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)]) - slatime = int(product_sla.sla) or 1 - max_slatime = max(max_slatime, slatime) - + @api.depends("order_line.product_id") + def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: + max_slatime = 1 # Default SLA jika tidak ada + for line in rec.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + max_slatime = max(max_slatime, slatime) + if rec.date_order: eta_date = datetime.now() + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date - if not rec.expected_ready_ship_date: - rec.expected_ready_ship_date = eta_date - # else: - # rec.estimated_ready_ship_date = False + # Jika expected_ready_to_ship kosong, set nilai default + if not rec.expected_ready_to_ship: + rec.expected_ready_to_ship = eta_date def _set_etrts_date(self): for order in self: @@ -413,19 +404,6 @@ class SaleOrder(models.Model): raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) - @api.onchange('estimated_ready_ship_date') - def _onchange_estimated_ready_ship_date(self): - for record in self: - if (record.estimated_ready_ship_date and record.expected_ready_ship_date): - if(record.estimated_ready_ship_date < record.expected_ready_ship_date): - return { - 'warning': { - 'title': _('Requested date is too soon.'), - 'message': _("The delivery date is sooner than the expected date." - "You may be unable to honor the delivery date.") - } - } - def _prepare_invoice(self): """ Prepare the dict of values to create the new invoice for a sales order. This method may be @@ -619,6 +597,21 @@ class SaleOrder(models.Model): if line.product_id.type == 'product': line_no += 1 line.line_no = line_no + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) def write(self, vals): res = super(SaleOrder, self).write(vals) @@ -627,7 +620,7 @@ class SaleOrder(models.Model): for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id - + return res def calculate_so_status(self): @@ -1433,7 +1426,7 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) - order._compute_etrts_date() + # order._compute_etrts_date() # order._update_partner_details() return order diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 09a71912..74fc6e54 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -69,7 +69,7 @@ - + -- cgit v1.2.3 From 8c87a6c35b2242ee1804e1955bbb216c8c86de4d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 10 Feb 2025 09:44:05 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cc86c451..ec761900 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1217,4 +1217,19 @@ class BarcodeProduct(models.Model): if record.barcode and not record.product_id.barcode: record.product_id.barcode = record.barcode else: - raise UserError('Barcode sudah terisi') \ No newline at end of file + raise UserError('Barcode sudah terisi') + +class CheckKoli(models.Model): + _name = 'check.koli' + _description = 'Check Koli' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + product_id = fields.(string='Koli') \ No newline at end of file -- cgit v1.2.3 From c99bf4c49859450ce4cb081c920edda2077b3b1a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 11 Feb 2025 09:48:55 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 34 +++++++++++++++++++++++++- indoteknik_custom/security/ir.model.access.csv | 2 ++ indoteknik_custom/views/stock_picking.xml | 28 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ec761900..3f888b02 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,6 +16,8 @@ _logger = logging.getLogger(__name__) class StockPicking(models.Model): _inherit = 'stock.picking' + scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True) + check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', auto_join=True) check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') @@ -122,6 +124,8 @@ class StockPicking(models.Model): ('cancel', 'Cancelled'), ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") + quantity_koli = fields.Float(string="Quantity Koli") + source_koli_id = fields.Many2one('stock.picking', string="Source Koli") @api.model def _compute_dokumen_tanda_terima(self): @@ -166,6 +170,14 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + @api.onchange('quantity_koli') + def _onchange_quantity_koli(self): + self.check_koli_lines = [(5, 0, 0)] + self.check_koli_lines = [(0, 0, { + 'koli': f"{self.name}/{str(i+1).zfill(3)}", + 'picking_id': self.id, + }) for i in range(int(self.quantity_koli))] + def _compute_lalamove_image_html(self): for record in self: if record.lalamove_image_url: @@ -1223,6 +1235,7 @@ class CheckKoli(models.Model): _name = 'check.koli' _description = 'Check Koli' _order = 'picking_id, id' + _rec_name = 'koli' picking_id = fields.Many2one( 'stock.picking', @@ -1232,4 +1245,23 @@ class CheckKoli(models.Model): index=True, copy=False, ) - product_id = fields.(string='Koli') \ No newline at end of file + koli = fields.Char(string='Koli') + +class ScanKoli(models.Model): + _name = 'scan.koli' + _description = 'Scan Koli' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + koli_id = fields.Many2one('check.koli', string='Koli') + + @api.constrains('koli_id') + def _constrains_koli_id(self): + self.picking_id.source_koli_id = self.koli_id.picking_id.id \ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 73877052..fa126492 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -150,6 +150,8 @@ access_v_move_outstanding,access.v.move.outstanding,model_v_move_outstanding,,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_check_product,access.check.product,model_check_product,,1,1,1,1 +access_check_koli,access.check.koli,model_check_koli,,1,1,1,1 +access_scan_koli,access.scan.koli,model_scan_koli,,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 diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 1c6ae8f7..42fa481d 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -72,6 +72,8 @@ + + @@ -198,11 +200,37 @@ + + + + + + + + scan.koli.tree + scan.koli + + + + + + + + + check.koli.tree + check.koli + + + + + + + check.product.tree check.product -- cgit v1.2.3 From 6a1b03cbd12931784aee8226ed5f163dcae42081 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 13 Feb 2025 11:20:11 +0700 Subject: biteship --- indoteknik_custom/models/sale_order.py | 35 ++++++++++++----------- indoteknik_custom/models/stock_picking.py | 46 +++++++++++++++++-------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index cae99447..c6f8adc4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -145,13 +145,13 @@ class SaleOrder(models.Model): ('NP', 'Non Pareto') ]) estimated_ready_ship_date = fields.Datetime( - string='ET Ready to Ship', + string='ET Ready to Ship compute', compute='_compute_etrts_date', store=True ) expected_ready_to_ship = fields.Datetime( - string='ET Ready to Ship FIX', + string='ET Ready to Ship', copy=False, store=True ) @@ -394,9 +394,26 @@ class SaleOrder(models.Model): if rec.date_order: eta_date = datetime.now() + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date + rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default if not rec.expected_ready_to_ship: rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + rec.commitment_date = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) def _set_etrts_date(self): for order in self: @@ -598,20 +615,6 @@ class SaleOrder(models.Model): line_no += 1 line.line_no = line_no - @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship - def _onchange_expected_ready_ship_date(self): - for rec in self: - if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: - # Hanya membandingkan tanggal saja, tanpa jam - expected_date = rec.expected_ready_to_ship.date() - estimated_date = rec.estimated_ready_ship_date.date() - - if expected_date < estimated_date: - rec.expected_ready_to_ship = rec.estimated_ready_ship_date - raise ValidationError( - "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." - .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) - ) def write(self, vals): res = super(SaleOrder, self).write(vals) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f766dc3f..e7d9dbd5 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -170,18 +170,18 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', store=True, default=False) + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_ready_to_ship', store=True, default=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_hours(self): - for record in self: - if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # Gunakan nilai yang sangat besar sebagai placeholder - record.countdown_hours = 999999 - else: - delta = record.estimated_ready_ship_date - waktu.now() - record.countdown_hours = delta.total_seconds() / 3600 + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 @api.depends('estimated_ready_ship_date', 'state') def _compute_countdown_ready_to_ship(self): @@ -194,6 +194,7 @@ class StockPicking(models.Model): days = delta.days hours, remainder = divmod(delta.seconds, 3600) record.countdown_ready_to_ship = f"{days} days, {hours} hours" + record.countdown_hours = delta.total_seconds() / 3600 else: record.countdown_ready_to_ship = False @@ -351,7 +352,7 @@ class StockPicking(models.Model): 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 @@ -367,7 +368,7 @@ class StockPicking(models.Model): # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - + # Fungsi untuk membangun items_data dari order lines def build_items_data(lines): return [{ @@ -411,7 +412,8 @@ class StockPicking(models.Model): "destination_contact_phone": self.real_shipping_id.phone or self.real_shipping_id.mobile, "destination_address": self.real_shipping_id.street, "destination_postal_code": self.real_shipping_id.zip, - "courier_type": "reg", + "origin_note": "BELAKANG INDOMARET", + "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", "destination_postal_code": self.real_shipping_id.zip, @@ -421,11 +423,15 @@ class StockPicking(models.Model): # Cek jika pengiriman instant atau same_day if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "origin_note": "BELAKANG INDOMARET", - "courier_company": self.carrier_id.name.lower(), - "courier_type": self.sale_id.delivery_service_type, - "delivery_type": "now", - "items": items_data_instant # Gunakan items untuk instant + "origin_coordinate" :{ + "latitude": -6.3031123, + "longitude" : 106.7794934999 + }, + "destination_coordinate" : { + "latitude": self.real_shipping_id.latitude, + "longitude": self.real_shipping_id.longtitude, + }, + "items": items_data_instant }) headers = { @@ -440,8 +446,8 @@ class StockPicking(models.Model): data = response.json() self.biteship_id = data.get("id", "") - self.biteship_tracking_id = data.get("tracking_id", "") - self.biteship_waybill_id = data.get("waybill_id", "") + self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") + self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") return data else: -- cgit v1.2.3 From 99b252edaefc372fcd4ef59e065284d8feeb669c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 19 Feb 2025 09:38:46 +0700 Subject: push --- indoteknik_custom/models/__init__.py | 2 + indoteknik_custom/models/sale_order.py | 1 + indoteknik_custom/models/sales_order_koli.py | 25 +++ .../models/stock_backorder_confirmation.py | 50 ++++++ indoteknik_custom/models/stock_picking.py | 171 ++++++++++++++++++++- indoteknik_custom/security/ir.model.access.csv | 2 + indoteknik_custom/views/sale_order.xml | 15 ++ indoteknik_custom/views/stock_picking.xml | 6 +- 8 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 indoteknik_custom/models/sales_order_koli.py create mode 100644 indoteknik_custom/models/stock_backorder_confirmation.py diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index ed9e91da..a5297806 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -140,3 +140,5 @@ from . import va_multi_reject from . import stock_immediate_transfer from . import coretax_fatur from . import barcoding_product +from . import sales_order_koli +from . import stock_backorder_confirmation diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 7b2d9bf8..88c32fb6 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -11,6 +11,7 @@ _logger = logging.getLogger(__name__) class SaleOrder(models.Model): _inherit = "sale.order" + koli_lines = fields.One2many('sales.order.koli', 'sale_order_id', string='Sales Order Koli', auto_join=True) fulfillment_line_v2 = fields.One2many('sales.order.fulfillment.v2', 'sale_order_id', string='Fullfillment2') fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment') reject_line = fields.One2many('sales.order.reject', 'sale_order_id', string='Reject Lines') diff --git a/indoteknik_custom/models/sales_order_koli.py b/indoteknik_custom/models/sales_order_koli.py new file mode 100644 index 00000000..02e85256 --- /dev/null +++ b/indoteknik_custom/models/sales_order_koli.py @@ -0,0 +1,25 @@ +from odoo import fields, models, api, _ +from odoo.exceptions import AccessError, UserError, ValidationError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT +import logging + +_logger = logging.getLogger(__name__) + + +class SalesOrderKoli(models.Model): + _name = 'sales.order.koli' + _description = 'Sales Order Koli' + _order = 'sale_order_id, id' + _rec_name = 'koli_id' + + sale_order_id = fields.Many2one( + 'sale.order', + string='Sale Order Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + koli_id = fields.Many2one('check.koli', string='Koli') + picking_id = fields.Many2one('stock.picking', string='Picking') + diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py new file mode 100644 index 00000000..0fd7c34e --- /dev/null +++ b/indoteknik_custom/models/stock_backorder_confirmation.py @@ -0,0 +1,50 @@ +from odoo import models, fields, api +from odoo.tools.float_utils import float_compare + +class StockBackorderConfirmation(models.TransientModel): + _inherit = 'stock.backorder.confirmation' + + def process(self): + res = super(StockBackorderConfirmation, self).process() + + pickings_to_do = self.env['stock.picking'] + for line in self.backorder_confirmation_line_ids: + if line.to_backorder: + pickings_to_do |= line.picking_id + + for pick in pickings_to_do: + # Mencari backorder yang baru terbentuk + backorder = self.env['stock.picking'].search([('backorder_id', '=', pick.id)], limit=1) + + if backorder: + # Cari BU/OUT terbaru berdasarkan sale_id + latest_out_picking = self.env['stock.picking'].search([ + ('sale_id', '=', pick.sale_id.id), + ('picking_type_id.code', '=', 'outgoing') + ], order='id desc', limit=1) + + # Update linked_out_picking_id pada backorder BU/PICK + if latest_out_picking: + backorder.linked_out_picking_id = latest_out_picking.id + else: + backorder.linked_out_picking_id = pick.linked_out_picking_id + + # 🚀 Cek apakah ada backorder baru dari BU/OUT + for pick in self.env['stock.picking'].search([ + ('picking_type_id.code', '=', 'outgoing'), + ('backorder_id', '!=', False) + ]): + # Backorder BU/OUT terbaru + latest_out_backorder = self.env['stock.picking'].search([ + ('backorder_id', '=', pick.id) + ], order='id desc', limit=1) + + if latest_out_backorder: + # 🚀 Update semua BU/PICK yang belum `done` atau `cancel` + self.env['stock.picking'].search([ + ('sale_id', '=', pick.sale_id.id), + ('picking_type_id.code', '=', 'incoming'), + ('state', 'not in', ['done', 'cancel']) + ]).write({'linked_out_picking_id': latest_out_backorder.id}) + + return res diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3f888b02..02ce819f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -124,7 +124,7 @@ class StockPicking(models.Model): ('cancel', 'Cancelled'), ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") - quantity_koli = fields.Float(string="Quantity Koli") + quantity_koli = fields.Float(string="Quantity Koli", copy=False) source_koli_id = fields.Many2one('stock.picking', string="Source Koli") @api.model @@ -170,6 +170,54 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli") + total_koli = fields.Integer(compute='_compute_total_koli', string="Total Koli") + total_koli_display = fields.Char(compute='_compute_total_koli_display', string="Total Koli Display") + linked_out_picking_id = fields.Many2one('stock.picking', string="Linked BU/OUT", copy=False) + backorder_picking_id = fields.Many2one('stock.picking', string="Backorder Picking", copy=False) + + def action_create_backorder(self): + """ Override method to handle backorder logic automatically """ + backorder = super(StockPicking, self).action_create_backorder() + + for picking in self: + if 'BU/PICK/' in picking.name: + # Jika BU/PICK memiliki BU/OUT yang terhubung + if picking.linked_out_picking_id: + out_picking = picking.linked_out_picking_id + out_backorder = out_picking.backorder_picking_id + + # Jika BU/OUT belum punya backorder, hubungkan BU/PICK backorder ke BU/OUT lama + if not out_backorder: + backorder.linked_out_picking_id = out_picking + else: + # Jika BU/OUT sudah punya backorder, hubungkan ke backorder BU/OUT + backorder.linked_out_picking_id = out_backorder + + elif 'BU/OUT/' in picking.name: + # Jika BU/OUT membuat backorder, update semua BU/PICK yang terhubung ke BU/OUT lama + pickings_to_update = self.env['stock.picking'].search([('linked_out_picking_id', '=', picking.id)]) + for pick in pickings_to_update: + pick.linked_out_picking_id = backorder + + return backorder + + + @api.depends('total_so_koli') # Sesuaikan dengan field yang relevan + def _compute_total_so_koli(self): + for picking in self: + picking.total_so_koli = self.env['check.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id)]) + + @api.depends('total_koli') # Sesuaikan dengan field yang relevan + def _compute_total_koli(self): + for picking in self: + picking.total_koli = self.env['scan.koli'].search_count([('picking_id', '=', picking.id)]) + + @api.depends('total_koli', 'total_so_koli') + def _compute_total_koli_display(self): + for picking in self: + picking.total_koli_display = f"{picking.total_koli} / {picking.total_so_koli}" + @api.onchange('quantity_koli') def _onchange_quantity_koli(self): self.check_koli_lines = [(5, 0, 0)] @@ -772,6 +820,9 @@ class StockPicking(models.Model): raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): + if self.total_koli != self.total_so_koli: + raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") + % (self.total_koli, self.total_so_koli)) 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") @@ -819,11 +870,40 @@ class StockPicking(models.Model): self.validation_minus_onhand_quantity() self.responsible = self.env.user.id + if self.picking_type_code == 'internal' and 'BU/PICK/' in self.name: + self.send_koli_to_so() + if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name: + self.check_koli() res = super(StockPicking, self).button_validate() self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' return res + + + def check_koli(self): + for picking in self: + sale_id = picking.sale_id + for koli_lines in picking.scan_koli_lines: + if koli_lines.koli_id.sale_order_id != sale_id: + raise UserError('Koli tidak sesuai') + + def send_koli_to_so(self): + for picking in self: + for koli_line in picking.check_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('picking_id', '=', picking.id), + ('koli_id', '=', koli_line.id) + ], limit=1) + + if not existing_koli: # Hindari duplikasi + self.env['sales.order.koli'].create({ + 'sale_order_id': picking.sale_id.id, + 'picking_id': picking.id, + 'koli_id': koli_line.id + }) + def check_qty_done_stock(self): @@ -901,6 +981,34 @@ class StockPicking(models.Model): res = super(StockPicking, self).action_cancel() return res + + def write(self, vals_list): + """ Override write method to auto-link BU/PICK to BU/OUT when necessary """ + records = super(StockPicking, self).write(vals_list) + for picking in records: + if 'BU/OUT/' in picking.name: + # Cari BU/PICK yang berhubungan berdasarkan logika tertentu + pick_picking = self.env['stock.picking'].search([ + ('name', 'like', 'BU/PICK/%'), + ('linked_out_picking_id', '=', False), + ('sale_id', '=', picking.sale_id.id) + ], limit=1) + + if pick_picking: + pick_picking.linked_out_picking_id = picking + + if 'BU/PICK/' in picking.name: + # Cari BU/PICK yang berhubungan berdasarkan logika tertentu + pick_picking = self.env['stock.picking'].search([ + ('name', 'like', 'BU/OUT/%'), + ('state', 'not in', ['cancel', 'done']), + ('sale_id', '=', picking.sale_id.id) + ], limit=1) + + if pick_picking: + pick_picking.linked_out_picking_id = picking + + return records @api.model @@ -917,7 +1025,20 @@ class StockPicking(models.Model): if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', vals.get('company_id'))]) > 0: new_name = f"{new_name}-DUP" vals['name'] = new_name - return super(StockPicking, self).create(vals) + records = super(StockPicking, self).create(vals) + for picking in records: + if 'BU/OUT/' in picking.name: + # Cari BU/PICK yang berhubungan berdasarkan logika tertentu + pick_picking = self.env['stock.picking'].search([ + ('name', 'like', 'BU/PICK'), + ('linked_out_picking_id', '=', False), + ('origin', '=', picking.origin) + ], limit=1) + + if pick_picking: + pick_picking.linked_out_picking_id = picking + + return records def write(self, vals): self._use_faktur(vals) @@ -1251,6 +1372,7 @@ class ScanKoli(models.Model): _name = 'scan.koli' _description = 'Scan Koli' _order = 'picking_id, id' + _rec_name = 'koli_id' picking_id = fields.Many2one( 'stock.picking', @@ -1260,8 +1382,45 @@ class ScanKoli(models.Model): index=True, copy=False, ) - koli_id = fields.Many2one('check.koli', string='Koli') + koli_id = fields.Many2one('sales.order.koli', string='Koli') + scan_koli_progress = fields.Char( + string="Progress Scan Koli", + compute="_compute_scan_koli_progress" + ) + + def _compute_scan_koli_progress(self): + """ Menghitung progres scan koli dalam format 'X/Y' """ + for scan in self: + if scan.picking_id: + all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') + scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan + total_so_koli = scan.picking_id.total_so_koli + scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" + + @api.model_create_multi + def create(self, vals_list): + """ Override create untuk update progress scan setelah scan koli ditambahkan """ + records = super(ScanKoli, self).create(vals_list) + for record in records: + record._compute_scan_koli_progress() + return records + + @api.constrains('picking_id', 'picking_id.total_so_koli') + def _check_koli_validation(self): + """ Validasi jika jumlah scan koli melebihi total SO koli """ + for scan in self: + total_scans = len(scan.picking_id.scan_koli_lines) + if total_scans > scan.picking_id.total_so_koli: + raise UserError(_("Jumlah scan koli melebihi total SO koli!")) + + @api.onchange('koli_id') + def _onchange_koli_id(self): + if not self.koli_id: + return + + source_koli_so = self.picking_id.ids # Picking asal dari Koli yang dipilih + source_koli = self.koli_id.picking_id.linked_out_picking_id.ids - @api.constrains('koli_id') - def _constrains_koli_id(self): - self.picking_id.source_koli_id = self.koli_id.picking_id.id \ No newline at end of file + # Cek apakah source_koli ditemukan + if source_koli_so != source_koli: + raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index fa126492..3040ff2f 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -155,6 +155,8 @@ access_scan_koli,access.scan.koli,model_scan_koli,,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_sales_order_koli,access.sales.order.koli,model_sales_order_koli,,1,1,1,1 +access_stock_backorder_confirmation,access.stock.backorder.confirmation,model_stock_backorder_confirmation,,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 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 008a04ed..877208b0 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -280,6 +280,9 @@ + + +
@@ -391,6 +394,18 @@ + + + sales.order.koli.tree + sales.order.koli + + + + + + + + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 42fa481d..2a11459c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -73,7 +73,10 @@ - + + + + @@ -217,6 +220,7 @@ + -- cgit v1.2.3 From 92222d326692652b2c4146e0e6040c74f75d4abc Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 21 Feb 2025 11:29:14 +0700 Subject: tracking --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 +- indoteknik_custom/models/stock_picking.py | 93 ++++++++++++++++++++-- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 110cde8a..2fc4d8a5 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -101,7 +101,7 @@ class StockPicking(controller.Controller): picking = picking_model.browse(id) if not picking: return self.response(None) - + hostori = picking.get_tracking_detail() return self.response(picking.get_tracking_detail()) @http.route(prefix + 'stock-picking//tracking', auth='public', method=['GET', 'OPTIONS']) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index e7d9dbd5..49c17788 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -12,8 +12,15 @@ import base64 import requests import time import logging +import re +from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) +_biteship_url = "https://api.biteship.com/v1" +_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" + + + class StockPicking(models.Model): _inherit = 'stock.picking' # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) @@ -363,9 +370,10 @@ class StockPicking(models.Model): 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" - + + if self.biteship_tracking_id: + raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") + # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) @@ -401,6 +409,7 @@ class StockPicking(models.Model): }) payload = { + "reference_id " : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -433,14 +442,15 @@ class StockPicking(models.Model): }, "items": items_data_instant }) - + + api_key = _biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } # Kirim request ke Biteship - response = requests.post(url, headers=headers, json=payload) + response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) if response.status_code == 200: data = response.json() @@ -448,6 +458,7 @@ class StockPicking(models.Model): self.biteship_id = data.get("id", "") self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") + self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") return data else: @@ -1016,8 +1027,19 @@ class StockPicking(models.Model): 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, 'eta': self.generate_eta_delivery(), + 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } + + if self.biteship_id : + histori = self.get_manifest_biteship() + response['manifests'] = histori.get("manifests", []) + response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False + response['status'] = self._map_status_biteship(histori.get("delivered")) + + response + + return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False @@ -1030,6 +1052,67 @@ class StockPicking(models.Model): return response + def get_manifest_biteship(self): + api_key = _biteship_api_key + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + + manifests = [] + + try: + # Kirim request ke Biteship + response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) + result = response.json() + if(result.get('success') == True): + history = result.get("history", []) + status = result.get("status", "") + + for entry in reversed(history): + manifests.append({ + "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), + "datetime": self._convert_to_local_time(entry["updated_at"]), + "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + }) + + return { + "manifests": manifests, + "delivered": status + } + + return manifests + except Exception as e : + _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") + return { 'error': str(e) } + + def _convert_to_local_time(self, iso_date): + try: + dt_with_tz = waktu.fromisoformat(iso_date) + utc_dt = dt_with_tz.astimezone(pytz.utc) + + local_tz = pytz.timezone("Asia/Jakarta") + local_dt = utc_dt.astimezone(local_tz) + + return local_dt.strftime("%Y-%m-%d %H:%M:%S") + except Exception as e: + return str(e) + + def _map_status_biteship(self, status): + status_mapping = { + "confirmed": "pending", + "scheduled": "pending", + "allocated": "pending", + "picking_up": "pending", + "picked": "shipment", + "cancelled": "cancelled", + "on_hold": "on_hold", + "dropping_off": "shipment", + "delivered": "completed" + } + return status_mapping.get(status, "Hubungi Admin") + def generate_eta_delivery(self): current_date = datetime.datetime.now() prepare_days = 3 -- cgit v1.2.3 From f0d995cc220cefffe65ce308ee234528ddc0d6ed Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 09:33:28 +0700 Subject: biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 7 ++++++- indoteknik_custom/models/public_holiday.py | 4 ++-- indoteknik_custom/views/public_holiday.xml | 2 -- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2fc4d8a5..3e58417f 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -136,4 +136,9 @@ class StockPicking(controller.Controller): return self.response({ 'name': picking_data.name - }) \ No newline at end of file + }) + + @http.route(prefix + 'n', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + def udpate_status_from_bitehsip(self, **kw): + picking_code = int(kw.get('picking_code', 0)) + \ No newline at end of file diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py index 70b7c53a..851d9080 100644 --- a/indoteknik_custom/models/public_holiday.py +++ b/indoteknik_custom/models/public_holiday.py @@ -6,6 +6,6 @@ class PublicHoliday(models.Model): _description = 'Public Holidays' name = fields.Char(string='Holiday Name', required=True) - start_date = fields.Date('Start Holiday Date', required=True) - end_date = fields.Date('End Holiday Date', required=True) + start_date = fields.Date('Date Holiday', required=True) + # end_date = fields.Date('End Holiday Date', required=True) # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml index d0b9c5d5..146c5b0b 100644 --- a/indoteknik_custom/views/public_holiday.xml +++ b/indoteknik_custom/views/public_holiday.xml @@ -22,7 +22,6 @@ - @@ -36,7 +35,6 @@ - -- cgit v1.2.3 From c1810b315d820a184db47d551b39700ce00d1440 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 24 Feb 2025 09:57:56 +0700 Subject: push wms --- indoteknik_custom/models/sales_order_koli.py | 1 + .../models/stock_backorder_confirmation.py | 63 +++---- .../models/stock_immediate_transfer.py | 8 +- indoteknik_custom/models/stock_picking.py | 192 +++++++++------------ indoteknik_custom/views/sale_order.xml | 2 + indoteknik_custom/views/stock_picking.xml | 5 +- 6 files changed, 110 insertions(+), 161 deletions(-) diff --git a/indoteknik_custom/models/sales_order_koli.py b/indoteknik_custom/models/sales_order_koli.py index 02e85256..c782a40e 100644 --- a/indoteknik_custom/models/sales_order_koli.py +++ b/indoteknik_custom/models/sales_order_koli.py @@ -22,4 +22,5 @@ class SalesOrderKoli(models.Model): ) koli_id = fields.Many2one('check.koli', string='Koli') picking_id = fields.Many2one('stock.picking', string='Picking') + state = fields.Selection([('not_delivered', 'Not Delivered'), ('delivered', 'Delivered')], string='Status', default='not_delivered') diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py index 0fd7c34e..f4da4cb5 100644 --- a/indoteknik_custom/models/stock_backorder_confirmation.py +++ b/indoteknik_custom/models/stock_backorder_confirmation.py @@ -5,46 +5,29 @@ class StockBackorderConfirmation(models.TransientModel): _inherit = 'stock.backorder.confirmation' def process(self): - res = super(StockBackorderConfirmation, self).process() - pickings_to_do = self.env['stock.picking'] + pickings_not_to_do = self.env['stock.picking'] for line in self.backorder_confirmation_line_ids: - if line.to_backorder: + line.picking_id.send_mail_bills() + line.picking_id.send_koli_to_so() + if line.to_backorder is True: pickings_to_do |= line.picking_id - - for pick in pickings_to_do: - # Mencari backorder yang baru terbentuk - backorder = self.env['stock.picking'].search([('backorder_id', '=', pick.id)], limit=1) - - if backorder: - # Cari BU/OUT terbaru berdasarkan sale_id - latest_out_picking = self.env['stock.picking'].search([ - ('sale_id', '=', pick.sale_id.id), - ('picking_type_id.code', '=', 'outgoing') - ], order='id desc', limit=1) - - # Update linked_out_picking_id pada backorder BU/PICK - if latest_out_picking: - backorder.linked_out_picking_id = latest_out_picking.id - else: - backorder.linked_out_picking_id = pick.linked_out_picking_id - - # 🚀 Cek apakah ada backorder baru dari BU/OUT - for pick in self.env['stock.picking'].search([ - ('picking_type_id.code', '=', 'outgoing'), - ('backorder_id', '!=', False) - ]): - # Backorder BU/OUT terbaru - latest_out_backorder = self.env['stock.picking'].search([ - ('backorder_id', '=', pick.id) - ], order='id desc', limit=1) - - if latest_out_backorder: - # 🚀 Update semua BU/PICK yang belum `done` atau `cancel` - self.env['stock.picking'].search([ - ('sale_id', '=', pick.sale_id.id), - ('picking_type_id.code', '=', 'incoming'), - ('state', 'not in', ['done', 'cancel']) - ]).write({'linked_out_picking_id': latest_out_backorder.id}) - - return res + else: + pickings_not_to_do |= line.picking_id + + for pick_id in pickings_not_to_do: + moves_to_log = {} + for move in pick_id.move_lines: + if float_compare(move.product_uom_qty, + move.quantity_done, + precision_rounding=move.product_uom.rounding) > 0: + moves_to_log[move] = (move.quantity_done, move.product_uom_qty) + pick_id._log_less_quantities_than_expected(moves_to_log) + + 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).with_context(skip_backorder=True) + if pickings_not_to_do: + pickings_to_validate = pickings_to_validate.with_context(picking_ids_not_to_backorder=pickings_not_to_do.ids) + return pickings_to_validate.button_validate() + return True diff --git a/indoteknik_custom/models/stock_immediate_transfer.py b/indoteknik_custom/models/stock_immediate_transfer.py index 4be0dff2..ec00df7b 100644 --- a/indoteknik_custom/models/stock_immediate_transfer.py +++ b/indoteknik_custom/models/stock_immediate_transfer.py @@ -5,25 +5,25 @@ 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: + line.picking_id.send_mail_bills() + line.picking_id.send_koli_to_so() 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 @@ -33,4 +33,6 @@ class StockImmediateTransfer(models.TransientModel): 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_picking.py b/indoteknik_custom/models/stock_picking.py index 02ce819f..f3af00d9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -125,7 +125,6 @@ class StockPicking(models.Model): ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") quantity_koli = fields.Float(string="Quantity Koli", copy=False) - source_koli_id = fields.Many2one('stock.picking', string="Source Koli") @api.model def _compute_dokumen_tanda_terima(self): @@ -170,45 +169,17 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") - total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli") total_koli = fields.Integer(compute='_compute_total_koli', string="Total Koli") total_koli_display = fields.Char(compute='_compute_total_koli_display', string="Total Koli Display") linked_out_picking_id = fields.Many2one('stock.picking', string="Linked BU/OUT", copy=False) - backorder_picking_id = fields.Many2one('stock.picking', string="Backorder Picking", copy=False) - - def action_create_backorder(self): - """ Override method to handle backorder logic automatically """ - backorder = super(StockPicking, self).action_create_backorder() - - for picking in self: - if 'BU/PICK/' in picking.name: - # Jika BU/PICK memiliki BU/OUT yang terhubung - if picking.linked_out_picking_id: - out_picking = picking.linked_out_picking_id - out_backorder = out_picking.backorder_picking_id - - # Jika BU/OUT belum punya backorder, hubungkan BU/PICK backorder ke BU/OUT lama - if not out_backorder: - backorder.linked_out_picking_id = out_picking - else: - # Jika BU/OUT sudah punya backorder, hubungkan ke backorder BU/OUT - backorder.linked_out_picking_id = out_backorder - - elif 'BU/OUT/' in picking.name: - # Jika BU/OUT membuat backorder, update semua BU/PICK yang terhubung ke BU/OUT lama - pickings_to_update = self.env['stock.picking'].search([('linked_out_picking_id', '=', picking.id)]) - for pick in pickings_to_update: - pick.linked_out_picking_id = backorder - - return backorder - + total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli") - @api.depends('total_so_koli') # Sesuaikan dengan field yang relevan + @api.depends('total_so_koli') def _compute_total_so_koli(self): for picking in self: - picking.total_so_koli = self.env['check.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id)]) + picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')]) - @api.depends('total_koli') # Sesuaikan dengan field yang relevan + @api.depends('total_koli') def _compute_total_koli(self): for picking in self: picking.total_koli = self.env['scan.koli'].search_count([('picking_id', '=', picking.id)]) @@ -425,7 +396,7 @@ class StockPicking(models.Model): "name": order_line.product_id.name, "description": order_line.name, "value": order_line.price_unit, - "quantity": move_line.qty_done, # Menggunakan qty_done dari move_line + "quantity": move_line.qty_done, "weight": order_line.weight }) @@ -820,7 +791,7 @@ class StockPicking(models.Model): raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): - if self.total_koli != self.total_so_koli: + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") % (self.total_koli, self.total_so_koli)) if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': @@ -870,8 +841,7 @@ class StockPicking(models.Model): self.validation_minus_onhand_quantity() self.responsible = self.env.user.id - if self.picking_type_code == 'internal' and 'BU/PICK/' in self.name: - self.send_koli_to_so() + # self.send_koli_to_so() if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name: self.check_koli() res = super(StockPicking, self).button_validate() @@ -890,21 +860,29 @@ class StockPicking(models.Model): def send_koli_to_so(self): for picking in self: - for koli_line in picking.check_koli_lines: - existing_koli = self.env['sales.order.koli'].search([ - ('sale_order_id', '=', picking.sale_id.id), - ('picking_id', '=', picking.id), - ('koli_id', '=', koli_line.id) - ], limit=1) - - if not existing_koli: # Hindari duplikasi - self.env['sales.order.koli'].create({ - 'sale_order_id': picking.sale_id.id, - 'picking_id': picking.id, - 'koli_id': koli_line.id - }) - - + if picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name: + for koli_line in picking.check_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('picking_id', '=', picking.id), + ('koli_id', '=', koli_line.id) + ], limit=1) + + if not existing_koli: + self.env['sales.order.koli'].create({ + 'sale_order_id': picking.sale_id.id, + 'picking_id': picking.id, + 'koli_id': koli_line.id + }) + + if picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name: + for koli_line in picking.scan_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('koli_id', '=', koli_line.koli_id.koli_id.id) + ], limit=1) + + existing_koli.state = 'delivered' def check_qty_done_stock(self): for line in self.move_line_ids_without_package: @@ -981,62 +959,11 @@ class StockPicking(models.Model): res = super(StockPicking, self).action_cancel() return res - - def write(self, vals_list): - """ Override write method to auto-link BU/PICK to BU/OUT when necessary """ - records = super(StockPicking, self).write(vals_list) - for picking in records: - if 'BU/OUT/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/PICK/%'), - ('linked_out_picking_id', '=', False), - ('sale_id', '=', picking.sale_id.id) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking - - if 'BU/PICK/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/OUT/%'), - ('state', 'not in', ['cancel', 'done']), - ('sale_id', '=', picking.sale_id.id) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking - - return records - @api.model def create(self, vals): self._use_faktur(vals) - if vals.get('picking_type_code') == 'incoming' and vals.get('location_dest_id') == 58: - if 'name' in vals and vals['name'].startswith('BU/IN/'): - vals['name'] = vals['name'].replace('BU/IN/', 'BU/INPUT/', 1) - - if vals.get('picking_type_code') == 'internal' and vals.get('location_id') == 58: - if 'name' in vals and vals['name'].startswith('BU/INT'): - new_name = vals['name'].replace('BU/INT', 'BU/IN', 1) - # Periksa apakah nama sudah ada - if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', vals.get('company_id'))]) > 0: - new_name = f"{new_name}-DUP" - vals['name'] = new_name records = super(StockPicking, self).create(vals) - for picking in records: - if 'BU/OUT/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/PICK'), - ('linked_out_picking_id', '=', False), - ('origin', '=', picking.origin) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking return records @@ -1367,6 +1294,7 @@ class CheckKoli(models.Model): copy=False, ) koli = fields.Char(string='Koli') + reserved_id = fields.Many2one('stock.picking', string='Reserved Picking') class ScanKoli(models.Model): _name = 'scan.koli' @@ -1388,6 +1316,35 @@ class ScanKoli(models.Model): compute="_compute_scan_koli_progress" ) + def unlink(self): + for scan in self: + koli = scan.koli_id.koli_id + if koli: + # Hapus reserved_id saat scan dihapus + koli.reserved_id = False + + # Ambil semua scan koli yang masih ada dan memiliki picking_id yang sama + remaining_scans = self.env['scan.koli'].search([ + ('id', '!=', scan.id), # Kecuali scan yang sedang dihapus + ('koli_id.picking_id', '=', koli.picking_id.id) + ]) + + # Jika tidak ada scan lain yang memiliki picking_id yang sama, hapus linked_out_picking_id + if not remaining_scans: + koli.picking_id.linked_out_picking_id = False + + return super(ScanKoli, self).unlink() + + @api.onchange('koli_id','scan_koli_progress') + def onchange_koli_id(self): + if not self.koli_id: + return + + for scan in self: + if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: + scan.koli_id.koli_id.reserved_id = scan.picking_id.id.origin + scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin + def _compute_scan_koli_progress(self): """ Menghitung progres scan koli dalam format 'X/Y' """ for scan in self: @@ -1397,18 +1354,12 @@ class ScanKoli(models.Model): total_so_koli = scan.picking_id.total_so_koli scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" - @api.model_create_multi - def create(self, vals_list): - """ Override create untuk update progress scan setelah scan koli ditambahkan """ - records = super(ScanKoli, self).create(vals_list) - for record in records: - record._compute_scan_koli_progress() - return records - @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): """ Validasi jika jumlah scan koli melebihi total SO koli """ for scan in self: + scan.koli_id.koli_id.reserved_id = scan.picking_id.id + scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id total_scans = len(scan.picking_id.scan_koli_lines) if total_scans > scan.picking_id.total_so_koli: raise UserError(_("Jumlah scan koli melebihi total SO koli!")) @@ -1418,9 +1369,20 @@ class ScanKoli(models.Model): if not self.koli_id: return - source_koli_so = self.picking_id.ids # Picking asal dari Koli yang dipilih - source_koli = self.koli_id.picking_id.linked_out_picking_id.ids - - # Cek apakah source_koli ditemukan + source_koli_so = self.picking_id.group_id.id + source_koli = self.koli_id.picking_id.group_id.id + if source_koli_so != source_koli: raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + + @api.onchange('koli_id') + def _onchange_koliii(self): + if self.koli_id and self.picking_id: + existing_koli = self.env['scan.koli'].search([ + ('picking_id', '=', self.picking_id.id), + ('koli_id', '=', self.koli_id.id), + ('id', '!=', self.id.origin) # Hindari validasi saat edit data + ]) + if existing_koli: + self.koli_id = False # Reset field koli_id agar pengguna tidak bisa memilihnya + raise UserError(f"Koli {existing_koli.koli_id.name} sudah dipindai dalam picking ini!") \ No newline at end of file diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 877208b0..4d31b072 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -401,6 +401,8 @@ + + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 2a11459c..1b3406ec 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -73,10 +73,8 @@ - - @@ -219,7 +217,7 @@ scan.koli - + @@ -231,6 +229,7 @@ + -- cgit v1.2.3 From a9c4cd0c5ac694074f0e3a4359182a97f27f542e Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 11:26:24 +0700 Subject: webhook biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 3e58417f..01269724 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -138,7 +138,40 @@ class StockPicking(controller.Controller): 'name': picking_data.name }) - @http.route(prefix + 'n', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def udpate_status_from_bitehsip(self, **kw): - picking_code = int(kw.get('picking_code', 0)) + try: + data = request.jsonrequest # Ambil data JSON dari request + event = data.get('event') + + # Log Webhook ke Model Odoo + request.env['webhook.logs'].sudo().create({ + 'event': event, + 'order_id': data.get('order_id'), + 'courier_tracking_id': data.get('courier_tracking_id'), + 'courier_waybill_id': data.get('courier_waybill_id'), + 'status': data.get('status'), + 'order_price': data.get('price'), + 'cash_on_delivery_fee': data.get('cash_on_delivery_fee'), + 'proof_of_delivery_fee': data.get('proof_of_delivery_fee'), + 'shippment_fee': data.get('shippment_fee'), + }) + + # Handle Event Berdasarkan Jenisnya + if event == "order.status": + self.process_order_status(data) + elif event == "order.price": + self.process_order_price(data) + elif event == "order.waybill_id": + self.process_order_waybill(data) + + return {'success': True, 'message': f'Webhook {event} received'} + except Exception as e: + return {'success': False, 'message': str(e)} + + def process_order_status(self, data): + """Update status order di Odoo""" + order = request.env['sale.order'].sudo().search([('id', '=', data.get('order_id'))], limit=1) + if order: + order.write({'state': data.get('status')}) \ No newline at end of file -- cgit v1.2.3 From 1d2011c7b1b9766b0254479733b2ec226e8201bd Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 17:16:45 +0700 Subject: add public holiday --- indoteknik_api/controllers/api_v1/product.py | 15 ++------ indoteknik_api/controllers/api_v1/sale_order.py | 4 ++- indoteknik_custom/models/sale_order.py | 48 ++++++++++++++++++++----- indoteknik_custom/models/stock_picking.py | 22 ++++++------ indoteknik_custom/views/sale_order.xml | 2 ++ 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 93ef305c..557215ea 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -7,18 +7,7 @@ import logging import math import json -_logger = logging.getLogger(__name__) - -def get_days_until_next_business_day(start_date=None, *args, **kwargs): - today = start_date or datetime.today().date() - offset = 0 # Counter jumlah hari yang ditambahkan - - while today.weekday() >= 5 : - today += timedelta(days=1) - offset += 1 - - return offset - +_logger = logging.getLogger(__name__) class Product(controller.Controller): @@ -88,7 +77,7 @@ class Product(controller.Controller): break start_date = datetime.today().date() - additional_days = get_days_until_next_business_day(start_date) + additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) # Jika semua loop selesai tanpa include_instant menjadi False return self.response({ diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 8b95ade8..4afeb21b 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -386,7 +386,8 @@ class SaleOrder(controller.Controller): 'note_website': [], 'voucher': [], 'source': [], - 'estimated_arrival_days': ['number', 'default:0'] + 'estimated_arrival_days': ['number', 'default:0'], + 'estimated_arrival_days_start': ['number', 'default:0'] }) if not params['valid']: @@ -416,6 +417,7 @@ class SaleOrder(controller.Controller): 'partner_purchase_order_file': params['value']['po_file'], 'delivery_amt': params['value']['delivery_amount'] * 1.10, 'estimated_arrival_days': params['value']['estimated_arrival_days'], + 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], 'shipping_cost_covered': 'customer', 'shipping_paid_by': 'customer', 'carrier_id': params['value']['carrier_id'], diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d0b57a3d..d956e93a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -93,11 +93,13 @@ class SaleOrder(models.Model): applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) amount_voucher_shipping_disc = fields.Float(string='Voucher Discount') source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True) - estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0) + estimated_arrival_days = fields.Integer('Estimated Arrival To', default=0) + estimated_arrival_days_start = fields.Integer('Estimated Arrival From', default=0) email = fields.Char(string='Email') picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') - eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_start_date') + eta_date = fields.Datetime(string='ETA Date end', 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([ @@ -146,9 +148,7 @@ class SaleOrder(models.Model): ]) estimated_ready_ship_date = fields.Datetime( string='ET Ready to Ship compute', - compute='_compute_etrts_date', - store=True - + compute='_compute_etrts_date' ) expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship', @@ -156,7 +156,7 @@ class SaleOrder(models.Model): store=True ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') - + def _compute_shipping_method_picking(self): for order in self: if order.picking_ids: @@ -383,13 +383,43 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True - @api.depends('date_order', 'estimated_arrival_days', 'state') + @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): for rec in self: if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) else: rec.eta_date = False + rec.eta_date_start = False + + @api.depends('date_order', 'state', 'estimated_arrival_days_start') + def _compute_eta_start_date(self): + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days_start: + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) + else: + rec.eta_date_start = False + + def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + holiday = self.env['hr.public.holiday'] + + while True : + today += timedelta(days=1) + offset += 1 + + if today.weekday() >= 5: + continue + + is_holiday = holiday.search([("start_date", "=", today)]) + if is_holiday: + continue + + break + + return offset @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date @@ -401,7 +431,7 @@ class SaleOrder(models.Model): max_slatime = max(max_slatime, slatime) if rec.date_order: - eta_date = datetime.now() + timedelta(days=max_slatime) + eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default @@ -1470,7 +1500,7 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) - # order._compute_etrts_date() + order._compute_etrts_date() # order._update_partner_details() return order diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1690a4ed..be395cef 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -177,19 +177,19 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_ready_to_ship', store=True, default=False) + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', default=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - # @api.depends('estimated_ready_ship_date', 'state') - # def _compute_countdown_hours(self): - # for record in self: - # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # # Gunakan nilai yang sangat besar sebagai placeholder - # record.countdown_hours = 999999 - # else: - # delta = record.estimated_ready_ship_date - waktu.now() - # record.countdown_hours = delta.total_seconds() / 3600 + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_hours(self): + for record in self: + if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # Gunakan nilai yang sangat besar sebagai placeholder + record.countdown_hours = 999999 + else: + delta = record.estimated_ready_ship_date - waktu.now() + record.countdown_hours = delta.total_seconds() / 3600 @api.depends('estimated_ready_ship_date', 'state') def _compute_countdown_ready_to_ship(self): diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index b267eee4..4cc96cd2 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -68,6 +68,8 @@ + + -- cgit v1.2.3 From 0174a19b631d67a70805de45b252cdcf1c562fb6 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 25 Feb 2025 10:28:15 +0700 Subject: push wms --- indoteknik_custom/models/stock_immediate_transfer.py | 6 ++++-- indoteknik_custom/models/stock_picking.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_immediate_transfer.py b/indoteknik_custom/models/stock_immediate_transfer.py index ec00df7b..8724c567 100644 --- a/indoteknik_custom/models/stock_immediate_transfer.py +++ b/indoteknik_custom/models/stock_immediate_transfer.py @@ -9,14 +9,16 @@ class StockImmediateTransfer(models.TransientModel): pickings_not_to_do = self.env['stock.picking'] for line in self.immediate_transfer_line_ids: + line.picking_id.send_mail_bills() + line.picking_id.send_koli_to_so() if line.to_immediate is True: - line.picking_id.send_mail_bills() - line.picking_id.send_koli_to_so() pickings_to_do |= line.picking_id else: pickings_not_to_do |= line.picking_id for picking in pickings_to_do: + picking.send_mail_bills() + picking.send_koli_to_so() if picking.state == 'draft': picking.action_confirm() if picking.state != 'assigned': diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f3af00d9..f359a2fb 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -848,6 +848,7 @@ class StockPicking(models.Model): self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + self.send_koli_to_so() return res -- cgit v1.2.3 From 49a90fdef07cb9262eb43e63c7023e30925a3c0c Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 25 Feb 2025 14:29:52 +0700 Subject: webhook biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 37 ++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 9da9575b..15aac3cd 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -144,19 +144,6 @@ class StockPicking(controller.Controller): data = request.jsonrequest # Ambil data JSON dari request event = data.get('event') - # Log Webhook ke Model Odoo - request.env['webhook.logs'].sudo().create({ - 'event': event, - 'order_id': data.get('order_id'), - 'courier_tracking_id': data.get('courier_tracking_id'), - 'courier_waybill_id': data.get('courier_waybill_id'), - 'status': data.get('status'), - 'order_price': data.get('price'), - 'cash_on_delivery_fee': data.get('cash_on_delivery_fee'), - 'proof_of_delivery_fee': data.get('proof_of_delivery_fee'), - 'shippment_fee': data.get('shippment_fee'), - }) - # Handle Event Berdasarkan Jenisnya if event == "order.status": self.process_order_status(data) @@ -170,8 +157,26 @@ class StockPicking(controller.Controller): return {'success': False, 'message': str(e)} def process_order_status(self, data): - """Update status order di Odoo""" - order = request.env['sale.order'].sudo().search([('id', '=', data.get('order_id'))], limit=1) + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if data.get('status') == 'picked': + picking_model.write({'driver_departure_date': datetime.utcnow()}) + elif data.get('status') == 'delivered': + picking_model.write({'driver_arrival_date': datetime.utcnow()}) + + def process_order_price(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + order = request.env['sale.order'].sudo().search([('name', '=', picking_model.sale_id.name)], limit=1) if order: - order.write({'state': data.get('status')}) + order.write({ + 'delivery_amt': data.get('price') + }) + + def process_order_waybill(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if picking_model: + picking_model.write({ + 'biteship_waybill_id': data.get('courier_waybill_id'), + 'delivery_tracking_no': data.get('courier_waybill_id'), + 'biteship_tracking_id':data.get('courier_tracking_id') + }) \ No newline at end of file -- cgit v1.2.3 From 39da2566a2af32b3fdaeae1ce826e4f778e9b8ce Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 25 Feb 2025 14:30:39 +0700 Subject: ketinggalan --- indoteknik_custom/models/sale_order.py | 7 ------- indoteknik_custom/models/stock_picking.py | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d956e93a..43177f33 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -393,13 +393,6 @@ class SaleOrder(models.Model): rec.eta_date = False rec.eta_date_start = False - @api.depends('date_order', 'state', 'estimated_arrival_days_start') - def _compute_eta_start_date(self): - for rec in self: - if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days_start: - rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) - else: - rec.eta_date_start = False def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): today = start_date or datetime.today().date() diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index be395cef..00db6717 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1097,6 +1097,12 @@ class StockPicking(models.Model): def get_tracking_detail(self): self.ensure_one() + + order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) + + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start).strftime('%d %b') + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days).strftime('%d %b %Y') + formatted_eta = f"{eta_start} - {eta_end}" response = { 'delivery_order': { @@ -1109,7 +1115,7 @@ class StockPicking(models.Model): 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, - 'eta': self.generate_eta_delivery(), + 'eta': formatted_eta, 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } -- cgit v1.2.3 From 62caad158a936eee9a0b85fd4df0c664374b6bfb Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 26 Feb 2025 10:47:01 +0700 Subject: biteship tracking --- indoteknik_custom/models/sale_order.py | 2 +- indoteknik_custom/models/stock_picking.py | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 43177f33..634ea918 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -98,7 +98,7 @@ class SaleOrder(models.Model): email = fields.Char(string='Email') picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') - eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_start_date') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_date') eta_date = fields.Datetime(string='ETA Date end', 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') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 00db6717..605452e3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1099,15 +1099,12 @@ class StockPicking(models.Model): self.ensure_one() order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) - - eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start).strftime('%d %b') - eta_end = order.date_order + timedelta(days=order.estimated_arrival_days).strftime('%d %b %Y') - formatted_eta = f"{eta_start} - {eta_end}" response = { 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '', + 'service' : order.delivery_service_type or '', 'receiver_name': '', 'receiver_city': '' }, @@ -1115,19 +1112,21 @@ class StockPicking(models.Model): 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, - 'eta': formatted_eta, + 'eta': self.generate_eta_delivery(), 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } if self.biteship_id : histori = self.get_manifest_biteship() + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start) + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days) + formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" + response['eta'] = formatted_eta response['manifests'] = histori.get("manifests", []) response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False response['status'] = self._map_status_biteship(histori.get("delivered")) - response - return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: @@ -1155,6 +1154,15 @@ class StockPicking(models.Model): # Kirim request ke Biteship response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) result = response.json() + description = { + 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', + 'allocated' : 'Kurir akan melakukan pick-up pesanan', + 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("name", ""), + 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', + 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + 'delivered' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") + } if(result.get('success') == True): history = result.get("history", []) status = result.get("status", "") @@ -1163,7 +1171,8 @@ class StockPicking(models.Model): manifests.append({ "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), "datetime": self._convert_to_local_time(entry["updated_at"]), - "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + # "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + "description": description[entry["status"]], }) return { -- cgit v1.2.3 From e94dbdf4418c686ec4e8fdab41d4f05e5284fbfb Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 26 Feb 2025 13:41:50 +0700 Subject: push wms --- .../models/stock_immediate_transfer.py | 4 +- indoteknik_custom/models/stock_picking.py | 104 +++++++++++++-------- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/indoteknik_custom/models/stock_immediate_transfer.py b/indoteknik_custom/models/stock_immediate_transfer.py index 8724c567..35c17192 100644 --- a/indoteknik_custom/models/stock_immediate_transfer.py +++ b/indoteknik_custom/models/stock_immediate_transfer.py @@ -17,8 +17,8 @@ class StockImmediateTransfer(models.TransientModel): pickings_not_to_do |= line.picking_id for picking in pickings_to_do: - picking.send_mail_bills() - picking.send_koli_to_so() + # picking.send_mail_bills() + # picking.send_koli_to_so() if picking.state == 'draft': picking.action_confirm() if picking.state != 'assigned': diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f359a2fb..bbd9043d 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -4,6 +4,7 @@ from odoo.tools.float_utils import float_is_zero from datetime import timedelta, datetime from itertools import groupby import pytz, requests, json, requests +from collections import defaultdict from dateutil import parser import datetime import hmac @@ -1318,72 +1319,97 @@ class ScanKoli(models.Model): ) def unlink(self): + picking_ids = set(self.mapped('picking_id.id')) # Tangkap picking_id sebelum hapus scan.koli + for scan in self: koli = scan.koli_id.koli_id if koli: - # Hapus reserved_id saat scan dihapus koli.reserved_id = False - # Ambil semua scan koli yang masih ada dan memiliki picking_id yang sama - remaining_scans = self.env['scan.koli'].search([ - ('id', '!=', scan.id), # Kecuali scan yang sedang dihapus + # Jika tidak ada scan.koli lain untuk picking_id yang sama, reset linked_out_picking_id + if not self.env['scan.koli'].search_count([ + ('id', '!=', scan.id), ('koli_id.picking_id', '=', koli.picking_id.id) - ]) - - # Jika tidak ada scan lain yang memiliki picking_id yang sama, hapus linked_out_picking_id - if not remaining_scans: + ]): koli.picking_id.linked_out_picking_id = False - return super(ScanKoli, self).unlink() + result = super(ScanKoli, self).unlink() # Hapus scan.koli + + # Reset qty_done jika semua scan.koli untuk picking_id tersebut telah dihapus + for picking_id in picking_ids: + self._reset_qty_done_if_no_scan(picking_id) + + return result - @api.onchange('koli_id','scan_koli_progress') + def _reset_qty_done_if_no_scan(self, picking_id): + """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut.""" + remaining_scans = self.env['scan.koli'].search_count([('picking_id', '=', picking_id)]) + + if remaining_scans == 0: + picking = self.env['stock.picking'].browse(picking_id) + picking.move_line_ids_without_package.write({'qty_done': 0}) + picking.message_post(body=f"⚠️ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.") + + return remaining_scans + + @api.onchange('koli_id', 'scan_koli_progress') def onchange_koli_id(self): - if not self.koli_id: - return - - for scan in self: - if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: + for scan in self.filtered('koli_id'): + if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: scan.koli_id.koli_id.reserved_id = scan.picking_id.id.origin scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin + @api.depends('picking_id') def _compute_scan_koli_progress(self): - """ Menghitung progres scan koli dalam format 'X/Y' """ for scan in self: if scan.picking_id: - all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') - scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan total_so_koli = scan.picking_id.total_so_koli - scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" + scan.scan_koli_progress = f"{scan.picking_id.scan_koli_lines.ids.index(scan.id) + 1}/{total_so_koli}" if total_so_koli else "0/0" @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): - """ Validasi jika jumlah scan koli melebihi total SO koli """ for scan in self: scan.koli_id.koli_id.reserved_id = scan.picking_id.id scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id - total_scans = len(scan.picking_id.scan_koli_lines) - if total_scans > scan.picking_id.total_so_koli: - raise UserError(_("Jumlah scan koli melebihi total SO koli!")) + if len(scan.picking_id.scan_koli_lines) != scan.picking_id.total_so_koli: + raise UserError("Jumlah scan koli tidak sama dengan total SO koli!") @api.onchange('koli_id') def _onchange_koli_id(self): + for scan in self.filtered('koli_id'): + if scan.picking_id.group_id.id != scan.koli_id.picking_id.group_id.id: + raise UserError('Koli tidak sesuai, pastikan picking terkait benar!') + + @api.onchange('koli_id') + def _onchange_koliii(self): + for scan in self.filtered('koli_id'): + if self.env['scan.koli'].search_count([ + ('picking_id', '=', scan.picking_id.id), + ('koli_id', '=', scan.koli_id.id), + ('id', '!=', scan.id.origin) + ]): + scan.koli_id = False + raise UserError(f"Koli {scan.koli_id.name} sudah dipindai dalam picking ini!") + + @api.constrains('koli_id') + def _send_product_from_koli_id(self): if not self.koli_id: return - - source_koli_so = self.picking_id.group_id.id - source_koli = self.koli_id.picking_id.group_id.id - if source_koli_so != source_koli: - raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + koli_count_by_picking = defaultdict(int) + for scan in self: + koli_count_by_picking[scan.koli_id.picking_id.id] += 1 # Hitung jumlah koli per picking - @api.onchange('koli_id') - def _onchange_koliii(self): - if self.koli_id and self.picking_id: - existing_koli = self.env['scan.koli'].search([ - ('picking_id', '=', self.picking_id.id), - ('koli_id', '=', self.koli_id.id), - ('id', '!=', self.id.origin) # Hindari validasi saat edit data - ]) - if existing_koli: - self.koli_id = False # Reset field koli_id agar pengguna tidak bisa memilihnya - raise UserError(f"Koli {existing_koli.koli_id.name} sudah dipindai dalam picking ini!") \ No newline at end of file + for picking_id, total_koli in koli_count_by_picking.items(): + picking = self.env['stock.picking'].browse(picking_id) + + if total_koli == picking.quantity_koli: + # Ambil stock moves dari BU/PICK dan BU/OUT berdasarkan picking_id + pick_moves = self.env['stock.move.line'].search([('picking_id', '=', picking_id)]) + out_moves = self.env['stock.move.line'].search([('picking_id', '=', picking.linked_out_picking_id.id)]) + + # Sesuaikan product_id di BU/OUT dengan BU/PICK + for pick_move in pick_moves: + corresponding_out_move = out_moves.filtered(lambda m: m.product_id == pick_move.product_id) + if corresponding_out_move: + corresponding_out_move.qty_done = corresponding_out_move.product_uom_qty # Update qty_done \ No newline at end of file -- cgit v1.2.3 From ce7faa60bc86686c0fcabec9aebb7a35c8fd7395 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 27 Feb 2025 11:11:16 +0700 Subject: update source get vendor sla --- indoteknik_custom/models/vendor_sla.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 67b6ffc3..852baa7a 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -20,33 +20,36 @@ class VendorSLA(models.Model): # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration def generate_vendor_id_sla(self): - # Step 1: Group purchase orders by vendor (partner_id) - po_env = self.env['purchase.order'] - pos = po_env.read_group( - domain=[('state', '=', 'done')], - fields=['partner_id', 'date_planned', 'date_approve'], + # Step 1: Group stock pickings by vendor based on operation type + stock_picking_env = self.env['stock.picking'] + stock_moves = stock_picking_env.read_group( + domain=[ + ('state', '=', 'done'), + ('location_id', '=', 4), # Partner Locations/Vendors + ('location_dest_id', '=', 57) # BU/Stock + ], + fields=['partner_id', 'date_done', 'scheduled_date'], groupby=['partner_id'], lazy=False ) - for group in pos: + for group in stock_moves: partner_id = group['partner_id'][0] total_duration = 0 count = 0 # Step 2: Calculate the average duration for each vendor - pos_for_vendor = po_env.search([ + pos_for_vendor = stock_picking_env.search([ ('partner_id', '=', partner_id), ('state', '=', 'done'), - ('date_planned', '>=', '2023-01-01') ]) for po in pos_for_vendor: - if po.date_planned and po.date_approve: - date_planned = fields.Datetime.to_datetime(po.date_planned) - date_approve = fields.Datetime.to_datetime(po.date_approve) - if date_planned < date_approve: continue - duration = (date_planned - date_approve).total_seconds() / 3600 # Convert to hours + if po.date_done and po.purchase_id.date_approve: + date_of_transfer = fields.Datetime.to_datetime(po.date_done) + po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) + if date_of_transfer < po_confirmation_date: continue + duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours total_duration += duration count += 1 -- cgit v1.2.3 From d5e40546164b98fd9f819bc4f65f53d8b7c3c7f4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 28 Feb 2025 09:55:08 +0700 Subject: sequance --- indoteknik_custom/models/stock_picking.py | 105 +++++++++++++++++++++++------- indoteknik_custom/views/stock_picking.xml | 6 +- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 605452e3..696d25db 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -23,6 +23,8 @@ _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l class StockPicking(models.Model): _inherit = 'stock.picking' + _order = 'final_seq ASC' + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') @@ -178,33 +180,92 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', default=False) - countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - - @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_hours(self): + countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + final_seq_tmp = fields.Float(string='Sequance Order in hours', store=True, compute_sudo=True) + final_seq = fields.Float(string='Sequance Order', related='final_seq_tmp', index=True) + + execution_date = fields.Float( + string='Time Remainder by date Reserved', + store=True, # Menyimpan hasil ke database + ) + def _compute_execution_date_by_date_reserved(self, date_reserved): for record in self: - if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # Gunakan nilai yang sangat besar sebagai placeholder - record.countdown_hours = 999999 - else: - delta = record.estimated_ready_ship_date - waktu.now() - record.countdown_hours = delta.total_seconds() / 3600 - + try: + if record.date_reserved and record.state not in ('cancel', 'done'): + date_reserved = record.date_reserved + timedelta(days=1) + time_diff = (date_reserved - waktu.now()).total_seconds() / 3600 + record.execution_date = time_diff + else: + record.execution_date = 99999999999 # Kosongkan jika tidak memenuhi kondisi + + except Exception as e: + error = f"Error calculating sequance {str(e)}" + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + return { 'error': str(e) } + + + # @api.depends('date_reserved') + # def _callculate_final_sequance(self): + # filtered_records = self.filtered(lambda r: r.estimated_ready_ship_date and r.date_reserved and r.state not in ('cancel', 'done')) + # for record in filtered_records: + # estimated_by_erts = (record.estimated_ready_ship_date - waktu.now()).total_seconds() / 3600 + # estimated_by_date = (record.date_reserved - waktu.now()).total_seconds() / 3600 + # record.final_seq_tmp = min(estimated_by_erts, estimated_by_date) + + # (self - filtered_records).write({'final_seq_tmp': 99999999999}) + + @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_ready_to_ship(self): + def _callculate_sequance(self): for record in self: - if record.state in ('cancel', 'done'): - record.countdown_ready_to_ship = False - else: - if record.estimated_ready_ship_date: - delta = record.estimated_ready_ship_date - waktu.now() - days = delta.days - hours, remainder = divmod(delta.seconds, 3600) - record.countdown_ready_to_ship = f"{days} days, {hours} hours" - record.countdown_hours = delta.total_seconds() / 3600 + try : + if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + rts = record.estimated_ready_ship_date - waktu.now() + rts_days = rts.days + rts_hours = divmod(rts.seconds, 3600) + + estimated_by_erts = rts.total_seconds() / 3600 + + record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + record.countdown_hours = estimated_by_erts + record.final_seq = estimated_by_erts else: + + record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False + except Exception as e : + error = str(e) + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + + print(str(e)) + return { 'error': str(e) } + + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_ready_to_ship(self): + # for record in self: + # if record.state in ('cancel', 'done'): + # record.countdown_ready_to_ship = False + # else: + # if record.estimated_ready_ship_date: + # delta = record.estimated_ready_ship_date - waktu.now() + # days = delta.days + # hours, remainder = divmod(delta.seconds, 3600) + # record.countdown_ready_to_ship = f"{days} days, {hours} hours" + # record.countdown_hours = delta.total_seconds() / 3600 + # else: + # record.countdown_ready_to_ship = False def _compute_lalamove_image_html(self): for record in self: diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 1832c31e..61ee2610 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,8 +7,8 @@ - countdown_hours asc - + final_seq asc + @@ -18,7 +18,9 @@ + + -- cgit v1.2.3 From e9836504e2c814652165cd1a7055e91a8b6ea854 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 3 Mar 2025 13:15:17 +0700 Subject: CR customer commision jasper --- indoteknik_custom/models/commision.py | 65 +++++++++++++++++++++++--- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/customer_commision.xml | 50 ++++++++++++++++++-- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 6920154a..67cc5a62 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -1,4 +1,4 @@ -from odoo import models, api, fields +from odoo import models, api, fields, _ from odoo.exceptions import UserError from datetime import datetime import logging @@ -121,6 +121,21 @@ class CustomerRebate(models.Model): sum_dpp += invoice.price_subtotal return sum_dpp +class RejectReasonCommision(models.TransientModel): + _name = 'reject.reason.commision' + _description = 'Wizard for Reject Reason Customer Commision' + + request_id = fields.Many2one('customer.commision', string='Request') + reason_reject = fields.Text(string='Reason for Rejection', required=True, tracking=True) + + def confirm_reject(self): + commision = self.request_id + if commision: + commision.last_status = commision.status + commision.write({'reason_reject': self.reason_reject}) + commision.status = 'reject' + return {'type': 'ir.actions.act_window_close'} + class CustomerCommision(models.Model): _name = 'customer.commision' @@ -136,10 +151,23 @@ class CustomerCommision(models.Model): notification = fields.Char(string='Notification') commision_lines = fields.One2many('customer.commision.line', 'customer_commision_id', string='Lines', auto_join=True) status = fields.Selection([ + ('draft', 'Menunggu Approval Manager Sales'), + ('pengajuan1', 'Menunggu Approval Marketing'), + ('pengajuan2', 'Menunggu Approval Pimpinan'), + ('pengajuan3', 'Menunggu Approval Accounting'), + ('pengajuan4', 'Menunggu Approval Finnence'), + ('approved', 'Approved'), + ('reject', 'Rejected'), + ], string='Status', copy=False, readonly=True, tracking=3, index=True, track_visibility='onchange',default='draft') + last_status = fields.Selection([ + ('draft', 'Menunggu Approval Manager Sales'), ('pengajuan1', 'Menunggu Approval Marketing'), ('pengajuan2', 'Menunggu Approval Pimpinan'), - ('approved', 'Approved') - ], string='Status', copy=False, readonly=True, tracking=3) + ('pengajuan3', 'Menunggu Approval Accounting'), + ('pengajuan4', 'Menunggu Approval Finnence'), + ('approved', 'Approved'), + ('reject', 'Rejected'), + ], string='Status') commision_percent = fields.Float(string='Commision %', tracking=3) commision_amt = fields.Float(string='Commision Amount', tracking=3) total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp') @@ -148,15 +176,18 @@ class CustomerCommision(models.Model): ('cashback', 'Cashback'), ('rebate', 'Rebate'), ], string='Commision Type', required=True) - bank_name = fields.Char(string='Bank', tracking=3) - account_name = fields.Char(string='Account Name', tracking=3) - bank_account = fields.Char(string='Account No', tracking=3) + bank_name = fields.Char(string='Bank', tracking=3, required=True) + account_name = fields.Char(string='Account Name', tracking=3, required=True) + bank_account = fields.Char(string='Account No', tracking=3, required=True) note_transfer = fields.Char(string='Keterangan') brand_ids = fields.Many2many('x_manufactures', string='Brands') payment_status = fields.Selection([ ('pending', 'Pending'), ('payment', 'Payment'), ], string='Payment Status', copy=False, readonly=True, tracking=3, default='pending') + note_finnance = fields.Text('Notes Finnance') + reason_reject = fields.Char(string='Reason Reaject', tracking=True, track_visibility='onchange') + # add status for type of commision, fee, rebate / cashback # include child or not? @@ -220,11 +251,15 @@ class CustomerCommision(models.Model): return result def action_confirm_customer_commision(self):#add 2 step approval - if not self.status: + if not self.status or self.status == 'draft': self.status = 'pengajuan1' elif self.status == 'pengajuan1' and self.env.user.id == 19: self.status = 'pengajuan2' elif self.status == 'pengajuan2' and self.env.user.is_leader: + self.status = 'pengajuan3' + elif self.status == 'pengajuan3' and self.env.user.id == 1272: + self.status = 'pengajuan4' + elif self.status == 'pengajuan4' and self.env.user.id == 23: for line in self.commision_lines: line.invoice_id.is_customer_commision = True self.status = 'approved' @@ -232,6 +267,21 @@ class CustomerCommision(models.Model): raise UserError('Harus di approved oleh yang bersangkutan') return + def action_reject(self):#add 2 step approval + return { + 'type': 'ir.actions.act_window', + 'name': _('Reject Reason'), + 'res_model': 'reject.reason.commision', + 'view_mode': 'form', + 'target': 'new', + 'context': {'default_request_id': self.id}, + } + + def button_draft(self): + for commision in self: + commision.status = commision.last_status if commision.last_status else 'draft' + + def action_confirm_customer_payment(self): if self.status != 'approved': raise UserError('Commision harus di approve terlebih dahulu sebelum di konfirmasi pembayarannya') @@ -337,6 +387,7 @@ class CustomerCommisionLine(models.Model): total = fields.Float(string='Total') total_percent_margin = fields.Float('Total Margin', related='invoice_id.sale_id.total_percent_margin') product_id = fields.Many2one('product.product', string='Product') + sale_order_id = fields.Many2one('sale.order', string='Sale Order', related='invoice_id.sale_id') class AccountMove(models.Model): _inherit = 'account.move' diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 75b3bf48..bedb032a 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -165,3 +165,4 @@ access_barcoding_product_line,access.barcoding.product.line,model_barcoding_prod access_account_payment_register,access.account.payment.register,model_account_payment_register,,1,1,1,1 access_stock_inventory,access.stock.inventory,model_stock_inventory,,1,1,1,1 access_cancel_reason_order,cancel.reason.order,model_cancel_reason_order,,1,1,1,0 +access_reject_reason_commision,reject.reason.commision,model_reject_reason_commision,,1,1,1,0 diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 51172b1c..dddefed4 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -11,7 +11,7 @@ - + + @@ -43,14 +44,28 @@ customer.commision
+
-
@@ -75,7 +90,7 @@ - + @@ -92,6 +107,11 @@ + + + + +
@@ -102,6 +122,30 @@ + + + reject.reason.commision.form + reject.reason.commision + + + + + +
+
+ +
+
+ + + Reject Reason + reject.reason.commision + form + new + + customer.commision.list.select customer.commision -- cgit v1.2.3 From 20a56a76c519eba82f70ea1443e272ac64797dd9 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 3 Mar 2025 16:51:05 +0700 Subject: push --- .../models/stock_backorder_confirmation.py | 2 +- indoteknik_custom/models/stock_picking.py | 195 +++++++++++++++------ indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/stock_picking.xml | 49 ++++-- 4 files changed, 181 insertions(+), 66 deletions(-) diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py index f4da4cb5..d8a41f54 100644 --- a/indoteknik_custom/models/stock_backorder_confirmation.py +++ b/indoteknik_custom/models/stock_backorder_confirmation.py @@ -9,7 +9,7 @@ class StockBackorderConfirmation(models.TransientModel): pickings_not_to_do = self.env['stock.picking'] for line in self.backorder_confirmation_line_ids: line.picking_id.send_mail_bills() - line.picking_id.send_koli_to_so() + # line.picking_id.send_koli_to_so() if line.to_backorder is True: pickings_to_do |= line.picking_id else: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index bbd9043d..dfc33caa 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,10 +1,10 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero +from collections import defaultdict from datetime import timedelta, datetime from itertools import groupby import pytz, requests, json, requests -from collections import defaultdict from dateutil import parser import datetime import hmac @@ -127,6 +127,8 @@ class StockPicking(models.Model): notee = fields.Text(string="Note") quantity_koli = fields.Float(string="Quantity Koli", copy=False) + + @api.model def _compute_dokumen_tanda_terima(self): for picking in self: @@ -178,7 +180,10 @@ class StockPicking(models.Model): @api.depends('total_so_koli') def _compute_total_so_koli(self): for picking in self: - picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')]) + if picking.state == 'done': + picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '=', 'delivered')]) + else: + picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')]) @api.depends('total_koli') def _compute_total_koli(self): @@ -190,6 +195,24 @@ class StockPicking(models.Model): for picking in self: picking.total_koli_display = f"{picking.total_koli} / {picking.total_so_koli}" + @api.constrains('quantity_koli') + def _constrains_quantity_koli(self): + for picking in self: + if not picking.linked_out_picking_id: + so_koli = self.env['sales.order.koli'].search([('picking_id', '=', picking.id)]) + + if so_koli: + so_koli.unlink() + + for rec in picking.check_koli_lines: + self.env['sales.order.koli'].create({ + 'sale_order_id': picking.sale_id.id, + 'picking_id': picking.id, + 'koli_id': rec.id, + }) + else: + raise UserError('Tidak Bisa Mengubah Quantity Koli Karena Koli Dari Picking Ini Sudah Dipakai Di BU/OUT!') + @api.onchange('quantity_koli') def _onchange_quantity_koli(self): self.check_koli_lines = [(5, 0, 0)] @@ -850,6 +873,31 @@ class StockPicking(models.Model): self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' self.send_koli_to_so() + if not self.env.context.get('skip_koli_check'): + for picking in self: + if picking.sale_id: + all_koli_ids = picking.sale_id.koli_lines.filtered(lambda k: k.state != 'delivered').ids + scanned_koli_ids = picking.scan_koli_lines.mapped('koli_id.id') + + missing_koli_ids = set(all_koli_ids) - set(scanned_koli_ids) + + if len(missing_koli_ids) > 0 and picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name: + missing_koli_names = picking.sale_id.koli_lines.filtered(lambda k: k.id in missing_koli_ids and k.state != 'delivered').mapped('display_name') + missing_koli_list = "\n".join(f"- {name}" for name in missing_koli_names) + + # Buat wizard modal warning + wizard = self.env['warning.modal.wizard'].create({ + 'message': f"Berikut Koli yang belum discan:\n{missing_koli_list}", + 'picking_id': picking.id, + }) + + return { + 'type': 'ir.actions.act_window', + 'res_model': 'warning.modal.wizard', + 'view_mode': 'form', + 'res_id': wizard.id, + 'target': 'new', + } return res @@ -876,15 +924,16 @@ class StockPicking(models.Model): 'picking_id': picking.id, 'koli_id': koli_line.id }) - + if picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name: - for koli_line in picking.scan_koli_lines: - existing_koli = self.env['sales.order.koli'].search([ - ('sale_order_id', '=', picking.sale_id.id), - ('koli_id', '=', koli_line.koli_id.koli_id.id) - ], limit=1) - - existing_koli.state = 'delivered' + if picking.state == 'done': + for koli_line in picking.scan_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('koli_id', '=', koli_line.koli_id.koli_id.id) + ], limit=1) + + existing_koli.state = 'delivered' def check_qty_done_stock(self): for line in self.move_line_ids_without_package: @@ -1319,78 +1368,83 @@ class ScanKoli(models.Model): ) def unlink(self): - picking_ids = set(self.mapped('picking_id.id')) # Tangkap picking_id sebelum hapus scan.koli - + picking_ids = set(self.mapped('koli_id.picking_id.id')) # Ambil semua picking_id yang terpengaruh for scan in self: koli = scan.koli_id.koli_id if koli: + # Hapus reserved_id saat scan dihapus koli.reserved_id = False - # Jika tidak ada scan.koli lain untuk picking_id yang sama, reset linked_out_picking_id - if not self.env['scan.koli'].search_count([ - ('id', '!=', scan.id), - ('koli_id.picking_id', '=', koli.picking_id.id) - ]): - koli.picking_id.linked_out_picking_id = False - - result = super(ScanKoli, self).unlink() # Hapus scan.koli - - # Reset qty_done jika semua scan.koli untuk picking_id tersebut telah dihapus + # Periksa ulang apakah masih ada scan.koli yang tersisa untuk setiap picking_id for picking_id in picking_ids: - self._reset_qty_done_if_no_scan(picking_id) + remaining_scans = self.env['sales.order.koli'].search_count([ + ('koli_id.picking_id', '=', picking_id) + ]) - return result + delete_koli = len(self.filtered(lambda rec: rec.koli_id.picking_id.id == picking_id)) - def _reset_qty_done_if_no_scan(self, picking_id): - """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut.""" - remaining_scans = self.env['scan.koli'].search_count([('picking_id', '=', picking_id)]) - if remaining_scans == 0: - picking = self.env['stock.picking'].browse(picking_id) - picking.move_line_ids_without_package.write({'qty_done': 0}) - picking.message_post(body=f"⚠️ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.") + # Jika tidak ada scan.koli lain yang tersisa, set linked_out_picking_id ke False + if remaining_scans == delete_koli: + picking = self.env['stock.picking'].browse(picking_id) + picking.linked_out_picking_id = False + else: + raise UserError(_("Tidak dapat menghapus scan koli, karena masih ada scan koli lain yang tersisa untuk picking ini.")) + + for picking_id in picking_ids: + self._reset_qty_done_if_no_scan(picking_id) + + # self.check_koli_not_balance() - return remaining_scans + return super(ScanKoli, self).unlink() - @api.onchange('koli_id', 'scan_koli_progress') + @api.onchange('koli_id','scan_koli_progress') def onchange_koli_id(self): - for scan in self.filtered('koli_id'): - if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: + if not self.koli_id: + return + + for scan in self: + if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: scan.koli_id.koli_id.reserved_id = scan.picking_id.id.origin scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin - @api.depends('picking_id') def _compute_scan_koli_progress(self): + """ Menghitung progres scan koli dalam format 'X/Y' """ for scan in self: if scan.picking_id: + all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') + scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan total_so_koli = scan.picking_id.total_so_koli - scan.scan_koli_progress = f"{scan.picking_id.scan_koli_lines.ids.index(scan.id) + 1}/{total_so_koli}" if total_so_koli else "0/0" + scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): - for scan in self: + """ Validasi jika jumlah scan koli melebihi total SO koli """ + for scan in self.picking_id.scan_koli_lines: scan.koli_id.koli_id.reserved_id = scan.picking_id.id scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id - if len(scan.picking_id.scan_koli_lines) != scan.picking_id.total_so_koli: - raise UserError("Jumlah scan koli tidak sama dengan total SO koli!") + + total_scans = len(self.picking_id.scan_koli_lines) + if total_scans != self.picking_id.total_so_koli: + raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!")) + + # def check_koli_not_balance(self): + # for scan in self: + # total_scancs = self.env['scan.koli'].search_count([('picking_id', '=', scan.picking_id.id), ('id', '!=', scan.id)]) + # if total_scancs != scan.picking_id.total_so_koli: + # raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!")) @api.onchange('koli_id') def _onchange_koli_id(self): - for scan in self.filtered('koli_id'): - if scan.picking_id.group_id.id != scan.koli_id.picking_id.group_id.id: - raise UserError('Koli tidak sesuai, pastikan picking terkait benar!') - - @api.onchange('koli_id') - def _onchange_koliii(self): - for scan in self.filtered('koli_id'): - if self.env['scan.koli'].search_count([ - ('picking_id', '=', scan.picking_id.id), - ('koli_id', '=', scan.koli_id.id), - ('id', '!=', scan.id.origin) - ]): - scan.koli_id = False - raise UserError(f"Koli {scan.koli_id.name} sudah dipindai dalam picking ini!") + if not self.koli_id: + return + source_koli_so = self.picking_id.group_id.id + source_koli = self.koli_id.picking_id.group_id.id + + if source_koli_so != source_koli: + raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + @api.constrains('koli_id') def _send_product_from_koli_id(self): if not self.koli_id: @@ -1412,4 +1466,35 @@ class ScanKoli(models.Model): for pick_move in pick_moves: corresponding_out_move = out_moves.filtered(lambda m: m.product_id == pick_move.product_id) if corresponding_out_move: - corresponding_out_move.qty_done = corresponding_out_move.product_uom_qty # Update qty_done \ No newline at end of file + corresponding_out_move.qty_done += pick_move.qty_done + + def _reset_qty_done_if_no_scan(self, picking_id): + """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut.""" + product_bu_pick = self.env['stock.move.line'].search([('picking_id', '=', picking_id)]) + + for move in product_bu_pick: + product_bu_out = self.env['stock.move.line'].search([('picking_id', '=', self.picking_id.id), ('product_id', '=', move.product_id.id)]) + for bu_out in product_bu_out: + bu_out.qty_done -= move.qty_done + # if remaining_scans == 0: + # picking = self.env['stock.picking'].browse(picking_id) + # picking.move_line_ids_without_package.write({'qty_done': 0}) + # picking.message_post(body=f"⚠ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.") + + # return remaining_scans + +class WarningModalWizard(models.TransientModel): + _name = 'warning.modal.wizard' + _description = 'Peringatan Koli Belum Diperiksa' + + name = fields.Char(default="⚠️ Perhatian!") + message = fields.Text() + picking_id = fields.Many2one('stock.picking') + + def action_continue(self): + """Lanjutkan validasi setelah menutup wizard""" + if self.picking_id: + return self.picking_id.with_context(skip_koli_check=True).button_validate() + return {'type': 'ir.actions.act_window_close'} + + diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 3040ff2f..83de8bda 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -157,6 +157,7 @@ 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_sales_order_koli,access.sales.order.koli,model_sales_order_koli,,1,1,1,1 access_stock_backorder_confirmation,access.stock.backorder.confirmation,model_stock_backorder_confirmation,,1,1,1,1 +access_warning_modal_wizard,access.warning.modal.wizard,model_warning_modal_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 diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 1b3406ec..016fbf17 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -72,9 +72,9 @@ - - - + + + @@ -147,7 +147,7 @@ - + @@ -195,7 +195,7 @@ - + @@ -217,7 +217,7 @@ scan.koli - + @@ -239,9 +239,9 @@ check.product - - - + + + @@ -278,6 +278,35 @@ - + + + + warning.modal.wizard.form + warning.modal.wizard + +
+ +
+

⚠️ Perhatian!

+
+ + + +
+
+
+
+
+
+ + + Peringatan Koli + warning.modal.wizard + form + + new + \ No newline at end of file -- cgit v1.2.3 From 60de643d4a5f19abc7bee34ccd2e6e6f6219a750 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 4 Mar 2025 20:44:46 +0700 Subject: CR renca banner --- indoteknik_api/controllers/api_v1/banner.py | 3 ++- indoteknik_custom/models/solr/x_banner_banner.py | 3 ++- indoteknik_custom/models/x_banner_banner.py | 3 ++- indoteknik_custom/views/x_banner_banner.xml | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/banner.py b/indoteknik_api/controllers/api_v1/banner.py index 308d2765..83d0dc14 100644 --- a/indoteknik_api/controllers/api_v1/banner.py +++ b/indoteknik_api/controllers/api_v1/banner.py @@ -41,7 +41,8 @@ class Banner(controller.Controller): 'group_by_week': banner.group_by_week, 'image': request.env['ir.attachment'].api_image('x_banner.banner', 'x_banner_image', banner.id), 'headline_banner': banner.x_headline_banner, - 'description_banner': banner.x_description_banner + 'description_banner': banner.x_description_banner, + 'keyword_banner': banner.x_keyword_banner } if banner.group_by_week and int(banner.group_by_week) < week_number and type == 'index-a-1': diff --git a/indoteknik_custom/models/solr/x_banner_banner.py b/indoteknik_custom/models/solr/x_banner_banner.py index 8452644c..aa6e0c2a 100644 --- a/indoteknik_custom/models/solr/x_banner_banner.py +++ b/indoteknik_custom/models/solr/x_banner_banner.py @@ -23,7 +23,7 @@ class XBannerBanner(models.Model): 'function_name': function_name }) - @api.constrains('x_name', 'x_url_banner', 'background_color', 'x_banner_image', 'x_banner_category', 'x_relasi_manufacture', 'x_sequence_banner', 'x_status_banner', 'sequence', 'group_by_week', 'headline_banner_s', 'description_banner_s') + @api.constrains('x_name', 'x_url_banner', 'background_color', 'x_banner_image', 'x_banner_category', 'x_relasi_manufacture', 'x_sequence_banner', 'x_status_banner', 'sequence', 'group_by_week', 'headline_banner_s', 'description_banner_s', 'x_keyword_banner') def _create_solr_queue_sync_brands(self): self._create_solr_queue('_sync_banners_to_solr') @@ -51,6 +51,7 @@ class XBannerBanner(models.Model): 'group_by_week': banners.group_by_week or '', 'headline_banner_s': banners.x_headline_banner or '', 'description_banner_s': banners.x_description_banner or '', + 'keyword_banner_s': banners.x_keyword_banner or '', }) self.solr().add([document]) banners.update_last_update_solr() diff --git a/indoteknik_custom/models/x_banner_banner.py b/indoteknik_custom/models/x_banner_banner.py index 810bdf39..16d54b02 100755 --- a/indoteknik_custom/models/x_banner_banner.py +++ b/indoteknik_custom/models/x_banner_banner.py @@ -25,4 +25,5 @@ class XBannerBanner(models.Model): ('4', '4') ], string='Group by Week') x_headline_banner = fields.Text(string="Headline Banner") - x_description_banner = fields.Text(string="Description Banner") \ No newline at end of file + x_description_banner = fields.Text(string="Description Banner") + x_keyword_banner = fields.Text(string="Keyword Banner") \ No newline at end of file diff --git a/indoteknik_custom/views/x_banner_banner.xml b/indoteknik_custom/views/x_banner_banner.xml index ec1e38a5..e40568cc 100755 --- a/indoteknik_custom/views/x_banner_banner.xml +++ b/indoteknik_custom/views/x_banner_banner.xml @@ -33,6 +33,7 @@ + -- cgit v1.2.3 From 660913a45a1efe08f308d405e1011efc9744c553 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 5 Mar 2025 10:14:34 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index dfc33caa..df91d451 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -815,6 +815,9 @@ class StockPicking(models.Model): raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): + if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") % (self.total_koli, self.total_so_koli)) -- cgit v1.2.3 From 5802838642db8bd0969d9bedc68606710f1ef4b5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 10:36:29 +0700 Subject: CR renca tempo --- indoteknik_api/controllers/api_v1/partner.py | 2 ++ indoteknik_custom/models/res_partner.py | 3 +++ indoteknik_custom/models/user_pengajuan_tempo.py | 2 ++ indoteknik_custom/models/user_pengajuan_tempo_request.py | 4 +++- indoteknik_custom/views/user_pengajuan_tempo.xml | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 307165b3..57d2fdf9 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -357,7 +357,9 @@ class Partner(controller.Controller): 'districtPengiriman': ['alias:district_id_pengiriman'], 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], 'zipPengiriman': ['alias:zip_pengiriman'], + 'PICBarangMobile': ['alias:pic_mobile'], 'invoicePicTittle': ['alias:invoice_pic_tittle'], + 'invoicePicMobile': ['alias:invoice_pic_mobile'], 'invoicePic': ['alias:invoice_pic'], 'streetInvoice': ['alias:street_invoice'], 'stateInvoice': ['alias:state_id_invoice'], diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 7e574a72..78380135 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -58,6 +58,7 @@ class ResPartner(models.Model): # Pengiriman pic_name = fields.Char(string='Nama PIC Penerimaan Barang') + pic_mobile = fields.Char(string='Nomor HP 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') @@ -254,6 +255,7 @@ class ResPartner(models.Model): # Pengiriman vals['pic_name'] = vals.get('pic_name', self.pic_name) + vals['pic_mobile'] = vals.get('pic_mobile', self.pic_mobile) 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) @@ -323,6 +325,7 @@ class ResPartner(models.Model): 'finance_mobile': vals.get('finance_mobile'), 'finance_email': vals.get('finance_email'), 'pic_name': vals.get('pic_name'), + 'pic_mobile': vals.get('pic_mobile'), 'street_pengiriman': vals.get('street_pengiriman'), 'state_id_pengiriman': vals.get('state_id_pengiriman'), 'city_id_pengiriman': vals.get('city_id_pengiriman'), diff --git a/indoteknik_custom/models/user_pengajuan_tempo.py b/indoteknik_custom/models/user_pengajuan_tempo.py index 0fdcdbeb..d10e7e81 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo.py +++ b/indoteknik_custom/models/user_pengajuan_tempo.py @@ -74,6 +74,7 @@ class UserPengajuanTempo(models.Model): # Pengiriman pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang') + pic_mobile = fields.Char(string='Nomor HP 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') @@ -83,6 +84,7 @@ class UserPengajuanTempo(models.Model): 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') + invoice_pic_mobile = fields.Char(string='Nomor HP 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') diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index be4293a0..8920d7c4 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -108,6 +108,7 @@ class UserPengajuanTempoRequest(models.Model): # Pengiriman pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_tittle', store=True, readonly=False) + pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_mobile', 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) @@ -292,7 +293,7 @@ class UserPengajuanTempoRequest(models.Model): 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', + @api.onchange('pic_tittle','pic_mobile', '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', @@ -302,6 +303,7 @@ class UserPengajuanTempoRequest(models.Model): 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_mobile = self.pic_mobile 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 diff --git a/indoteknik_custom/views/user_pengajuan_tempo.xml b/indoteknik_custom/views/user_pengajuan_tempo.xml index 7f1faa41..4eebe9e4 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo.xml @@ -53,6 +53,7 @@ + @@ -62,6 +63,7 @@ + -- cgit v1.2.3 From f53c699804806a83252901f5aa076c2f9ddcb8b4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:24:35 +0700 Subject: biteship --- indoteknik_custom/models/stock_picking.py | 39 ++----------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 85cdc7eb..95c94fad 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,41 +179,10 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.sale') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) - final_seq_tmp = fields.Float(string='Sequance Order in hours', store=True, compute_sudo=True) - final_seq = fields.Float(string='Sequance Order', related='final_seq_tmp', index=True) - - execution_date = fields.Float( - string='Time Remainder by date Reserved', - store=True, # Menyimpan hasil ke database - ) - def _compute_execution_date_by_date_reserved(self, date_reserved): - for record in self: - try: - if record.date_reserved and record.state not in ('cancel', 'done'): - date_reserved = record.date_reserved + timedelta(days=1) - time_diff = (date_reserved - waktu.now()).total_seconds() / 3600 - record.execution_date = time_diff - else: - record.execution_date = 99999999999 # Kosongkan jika tidak memenuhi kondisi - - except Exception as e: - error = f"Error calculating sequance {str(e)}" - _logger.error(f"Error calculating sequance {self.id}: {str(e)}") - return { 'error': str(e) } - - - # @api.depends('date_reserved') - # def _callculate_final_sequance(self): - # filtered_records = self.filtered(lambda r: r.estimated_ready_ship_date and r.date_reserved and r.state not in ('cancel', 'done')) - # for record in filtered_records: - # estimated_by_erts = (record.estimated_ready_ship_date - waktu.now()).total_seconds() / 3600 - # estimated_by_date = (record.date_reserved - waktu.now()).total_seconds() / 3600 - # record.final_seq_tmp = min(estimated_by_erts, estimated_by_date) - - # (self - filtered_records).write({'final_seq_tmp': 99999999999}) + final_seq = fields.Float(string='Sequance Order', index=True) @api.depends('estimated_ready_ship_date', 'state') @@ -229,15 +198,11 @@ class StockPicking(models.Model): record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" record.countdown_hours = estimated_by_erts - record.final_seq = estimated_by_erts else: - record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False except Exception as e : - error = str(e) _logger.error(f"Error calculating sequance {self.id}: {str(e)}") - print(str(e)) return { 'error': str(e) } -- cgit v1.2.3 From 8d577ade2d913506d9d3996894e7b839f850d36a Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:34:53 +0700 Subject: uodate key biteship --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 95c94fad..73aacdf4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -17,7 +17,7 @@ from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" -- cgit v1.2.3 From e3b5d1c9bdd8a764983b01442ddf041e8ade87bf Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:51:50 +0700 Subject: fixing error --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 73aacdf4..bde7402f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,7 +179,7 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.sale') + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Sequance Order', index=True) -- cgit v1.2.3 From 6b80ada3ebc5477b51666ccb41cf30184a1d1af0 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 14:01:45 +0700 Subject: udpate bugs --- indoteknik_custom/models/stock_picking.py | 5 +++++ indoteknik_custom/views/stock_picking.xml | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index bde7402f..17ef1228 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -183,6 +183,11 @@ class StockPicking(models.Model): countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Sequance Order', index=True) + + + def schduled_update_sequance(self): + query = "SELECT update_sequance_stock_picking();" + self.env.cr.execute(query) @api.depends('estimated_ready_ship_date', 'state') diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 61ee2610..9d19b97c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -18,7 +18,6 @@ - -- cgit v1.2.3 From 8a48cbf462ce04b5c4be6c7ff29d0193c92572e0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 14:12:45 +0700 Subject: update pengajuan tempo --- indoteknik_api/controllers/api_v1/partner.py | 20 +++++++++++++++++++- indoteknik_custom/models/res_partner.py | 3 +++ .../models/user_pengajuan_tempo_request.py | 4 +++- indoteknik_custom/views/res_partner.xml | 2 ++ .../views/user_pengajuan_tempo_request.xml | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 57d2fdf9..9bb015c4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -311,6 +311,7 @@ class Partner(controller.Controller): 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) + dokumen_prosedur = kw.get('dokumen_prosedur', False) params = self.get_request_params(kw, { @@ -350,6 +351,7 @@ class Partner(controller.Controller): # Pengiriman 'PICTittle': ['alias:pic_tittle'], + 'PICBarangMobile': ['alias:pic_mobile'], 'PICName': ['alias:pic_name'], 'streetPengiriman': ['alias:street_pengiriman'], 'statePengiriman': ['alias:state_id_pengiriman'], @@ -357,7 +359,6 @@ class Partner(controller.Controller): 'districtPengiriman': ['alias:district_id_pengiriman'], 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], 'zipPengiriman': ['alias:zip_pengiriman'], - 'PICBarangMobile': ['alias:pic_mobile'], 'invoicePicTittle': ['alias:invoice_pic_tittle'], 'invoicePicMobile': ['alias:invoice_pic_mobile'], 'invoicePic': ['alias:invoice_pic'], @@ -402,6 +403,23 @@ class Partner(controller.Controller): if partner_id: pengajuan_tempo.name_tempo = partner_id + # Prosedur Pengiriman + if dokumen_prosedur: + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['details']['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(dokumen_prosedur['details']['base64']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': dokumen_prosedur['details']['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.pengajuan.tempo', + 'res_id': pengajuan_tempo.id, + 'mimetype': mimetype + }) + pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] + + form_supplier_data = kw.get('formSupplier', False) if form_supplier_data: diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 78380135..56c69f95 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -66,6 +66,7 @@ class ResPartner(models.Model): subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan') zip_pengiriman = fields.Char(string="Zip") invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice') + invoice_pic_mobile = fields.Char(string='Nomor HP 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') @@ -263,6 +264,7 @@ class ResPartner(models.Model): 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['invoice_pic_mobile'] = vals.get('invoice_pic_mobile', self.invoice_pic_mobile) 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) @@ -333,6 +335,7 @@ class ResPartner(models.Model): 'subDistrict_id_pengiriman': vals.get('subDistrict_id_pengiriman'), 'zip_pengiriman': vals.get('zip_pengiriman'), 'invoice_pic': vals.get('invoice_pic'), + 'invoice_pic_mobile': vals.get('invoice_pic_mobile'), 'street_invoice': vals.get('street_invoice'), 'state_id_invoice': vals.get('state_id_invoice'), 'city_id_invoice': vals.get('city_id_invoice'), diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 8920d7c4..72580001 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -117,6 +117,7 @@ class UserPengajuanTempoRequest(models.Model): 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_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_mobile', 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) @@ -295,7 +296,7 @@ class UserPengajuanTempoRequest(models.Model): @api.onchange('pic_tittle','pic_mobile', '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', + 'invoice_pic_tittle','invoice_pic_mobile', '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') @@ -312,6 +313,7 @@ class UserPengajuanTempoRequest(models.Model): 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_mobile = self.invoice_pic_mobile 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 diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index af5e0db3..b87e616d 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -131,6 +131,7 @@ + @@ -140,6 +141,7 @@ + diff --git a/indoteknik_custom/views/user_pengajuan_tempo_request.xml b/indoteknik_custom/views/user_pengajuan_tempo_request.xml index 7063231b..3ab00ed9 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo_request.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo_request.xml @@ -102,6 +102,7 @@ + @@ -111,6 +112,7 @@ + -- cgit v1.2.3 From 59f5be7f2145530979dcb0d0ff23197a4aa0c589 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 5 Mar 2025 14:31:32 +0700 Subject: push --- indoteknik_api/controllers/api_v1/product.py | 81 ++++++-- indoteknik_api/controllers/api_v1/sale_order.py | 4 +- indoteknik_api/controllers/api_v1/stock_picking.py | 47 ++++- indoteknik_custom/__manifest__.py | 2 + indoteknik_custom/models/__init__.py | 2 + indoteknik_custom/models/product_sla.py | 133 ++++++++----- indoteknik_custom/models/product_template.py | 12 ++ indoteknik_custom/models/public_holiday.py | 11 ++ indoteknik_custom/models/sale_order.py | 101 ++++++++-- indoteknik_custom/models/stock_picking.py | 212 +++++++++++++++++++-- indoteknik_custom/models/vendor_sla.py | 98 ++++++++++ indoteknik_custom/views/product_product.xml | 8 + indoteknik_custom/views/product_sla.xml | 7 +- indoteknik_custom/views/public_holiday.xml | 55 ++++++ indoteknik_custom/views/sale_order.xml | 3 + indoteknik_custom/views/stock_picking.xml | 6 +- indoteknik_custom/views/vendor_sla.xml | 42 ++++ indoteknik_custom/views/x_banner_category.xml | 2 +- 18 files changed, 727 insertions(+), 99 deletions(-) create mode 100644 indoteknik_custom/models/public_holiday.py create mode 100644 indoteknik_custom/models/vendor_sla.py create mode 100644 indoteknik_custom/views/public_holiday.xml create mode 100644 indoteknik_custom/views/vendor_sla.xml diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 32362582..557215ea 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -1,13 +1,13 @@ from .. import controller from odoo import http -from odoo.http import request +from odoo.http import request, Response from datetime import datetime, timedelta import ast import logging import math import json -_logger = logging.getLogger(__name__) +_logger = logging.getLogger(__name__) class Product(controller.Controller): @@ -33,9 +33,64 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - - @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) + + @http.route(prefix + 'product/variants/sla', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() + def get_product_template_sla_by_id(self, **kwargs): + body_params = kwargs.get('ids') + + if not body_params: + return self.response('Failed', code=400, description='id is required') + + ids = [int(id.strip()) for id in body_params.split(',') if id.strip().isdigit()] + + sla_duration = 0 + sla_unit = 'Hari' + include_instant = True + products = request.env['product.product'].search([('id', 'in', ids)]) + if len(products) < 1: + return self.response( + 'Failed', + code=400, + description='Produk Tidak Di Temukan.' + ) + + product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) + if len(product_slas) < 1: + return self.response( + 'Failed', + code=400, + description='SLA Tidak Di Temukan.' + ) + + # Mapping SLA untuk mempermudah lookup + sla_map = {sla.product_variant_id.id: sla for sla in product_slas} + + for product in products: + product_sla = sla_map.get(product.id) + if product_sla: + sla_duration = max(sla_duration, int(product_sla.sla)) + sla_unit = product_sla.sla_vendor_id.unit + if product.qty_free_bandengan < 1 : + if product_sla.sla_vendor_id.unit != 'jam': + include_instant = False + break + + start_date = datetime.today().date() + additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) + + # Jika semua loop selesai tanpa include_instant menjadi False + return self.response({ + 'include_instant': include_instant, + 'sla_duration': sla_duration, + 'sla_additional_days': additional_days, + 'sla_total' : int(sla_duration) + int(additional_days), + 'sla_unit': 'Hari' if additional_days > 0 else sla_unit + } + ) + + @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): id = int(kw.get('id')) date_7_days_ago = datetime.now() - timedelta(days=7) @@ -49,10 +104,11 @@ class Product(controller.Controller): ], limit=1) qty_available = product.qty_free_bandengan - - if qty_available < 0: - qty_available = 0 - + + + if qty_available < 1 : + qty_available = 0 + qty = 0 sla_date = '-' @@ -74,24 +130,25 @@ class Product(controller.Controller): if qty_available > 0: qty = qty_available + total_adem + total_excell + sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = '2-4 Hari' + sla_date = product_sla.sla else: - sla_date = '3-7 Hari' + sla_date = product_sla.sla except: print('error') else: if qty_available > 0: qty = qty_available - sla_date = product_sla.sla or '-' + sla_date = product_sla.sla or 'Indent' elif qty_vendor > 0: qty = total_excell sla_date = '2-4 Hari' data = { 'qty': qty, - 'sla_date': sla_date, + 'sla_date': sla_date } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index a7e027c8..6815bf6c 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -386,7 +386,8 @@ class SaleOrder(controller.Controller): 'note_website': [], 'voucher': [], 'source': [], - 'estimated_arrival_days': ['number', 'default:0'] + 'estimated_arrival_days': ['number', 'default:0'], + 'estimated_arrival_days_start': ['number', 'default:0'] }) if not params['valid']: @@ -417,6 +418,7 @@ class SaleOrder(controller.Controller): 'partner_purchase_order_file': params['value']['po_file'], 'delivery_amt': params['value']['delivery_amount'] * 1.10, 'estimated_arrival_days': params['value']['estimated_arrival_days'], + 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], 'shipping_cost_covered': 'customer', 'shipping_paid_by': 'customer', 'carrier_id': params['value']['carrier_id'], diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2e0c4ad0..15aac3cd 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -101,7 +101,7 @@ class StockPicking(controller.Controller): picking = picking_model.browse(id) if not picking: return self.response(None) - + hostori = picking.get_tracking_detail() return self.response(picking.get_tracking_detail()) @http.route(prefix + 'stock-picking//tracking', auth='public', method=['GET', 'OPTIONS']) @@ -136,4 +136,47 @@ class StockPicking(controller.Controller): return self.response({ 'name': picking_data.name - }) \ No newline at end of file + }) + + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) + def udpate_status_from_bitehsip(self, **kw): + try: + data = request.jsonrequest # Ambil data JSON dari request + event = data.get('event') + + # Handle Event Berdasarkan Jenisnya + if event == "order.status": + self.process_order_status(data) + elif event == "order.price": + self.process_order_price(data) + elif event == "order.waybill_id": + self.process_order_waybill(data) + + return {'success': True, 'message': f'Webhook {event} received'} + except Exception as e: + return {'success': False, 'message': str(e)} + + def process_order_status(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if data.get('status') == 'picked': + picking_model.write({'driver_departure_date': datetime.utcnow()}) + elif data.get('status') == 'delivered': + picking_model.write({'driver_arrival_date': datetime.utcnow()}) + + def process_order_price(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + order = request.env['sale.order'].sudo().search([('name', '=', picking_model.sale_id.name)], limit=1) + if order: + order.write({ + 'delivery_amt': data.get('price') + }) + + def process_order_waybill(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if picking_model: + picking_model.write({ + 'biteship_waybill_id': data.get('courier_waybill_id'), + 'delivery_tracking_no': data.get('courier_waybill_id'), + 'biteship_tracking_id':data.get('courier_tracking_id') + }) + \ No newline at end of file diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index f66314fa..a7096346 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -161,7 +161,9 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'views/vendor_sla.xml', 'views/coretax_faktur.xml', + 'views/public_holiday.xml', 'views/stock_inventory.xml', ], 'demo': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 3573eddd..37a49332 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -139,8 +139,10 @@ from . import user_pengajuan_tempo from . import approval_retur_picking from . import va_multi_approve from . import va_multi_reject +from . import vendor_sla from . import stock_immediate_transfer from . import coretax_fatur +from . import public_holiday from . import ir_actions_report from . import barcoding_product from . import account_payment_register diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 2e663d30..04ad2ffd 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,73 +12,110 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') - avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) - leadtime = fields.Char(string='Leadtime', readonly=True) + sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) + sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') + sla_logistic = fields.Char(string='SLA Logistic', readonly=True) + sla_logistic_unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Logistic Time" + ) + sla_logistic_duration_unit = fields.Char(string="SLA Logistic Duration (Unit)") sla = fields.Char(string='SLA', readonly=True) version = fields.Integer(string="Version", compute="_compute_version") def _compute_version(self): for sla in self: sla.version = sla.product_variant_id.sla_version + + - def generate_product_variant_id_sla(self, limit=5000): - # Filter produk non-Altama + def generate_product_variant_id_sla(self, limit=500): + offset = 0 + # while True: products = self.env['product.product'].search([ - ('x_manufacture', 'not in', [10, 122, 89]), - ('location_id', '=', 57), - ('stock_move_ids', '!=', False), - ], order='sla_version asc', limit=limit) + ('active', '=', True), + ('sale_ok', '=', True), + ], order='sla_version asc', limit=limit, offset=offset) + + # if not products: + # break - i = 1 for product in products: - _logger.info(f'Product SLA: {i}/{len(products)}') - i += 1 - product.sla_version += 1 + _logger.info(f'Memproses SLA untuk produk ID {product.id}, versi {product.sla_version}') product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) if not product_sla: - product_sla = self.env['product.sla'].create({ - 'product_variant_id': product.id, - }) - + product_sla = self.create({'product_variant_id': product.id}) + product_sla.generate_product_sla() + # Tandai produk sebagai sudah diproses + product.sla_version += 1 + + offset += limit + + def generate_product_sla(self): - self.avg_leadtime = '-' - self.sla = '-' + # self.sla_logistic = '-' + # self.sla_logistic_duration_unit = '-' + # self.sla = '-' product = self.product_variant_id - - qty_available = 0 - qty_available = product.qty_onhand_bandengan + + q_vendor = [ + ('product_id', '=', product.id), + ('is_winner', '=', True) + ] + + vendor = self.env['purchase.pricelist'].search(q_vendor) + vendor_duration = 0 - if qty_available > 0: - self.sla = '1 Hari' + #SLA Vendor + if vendor: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 + if sla_vendor > 0: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration * 60 + + self.sla_vendor_id = vendor_sla.id if vendor_sla else False + #SLA Logistik selalu 1 hari + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = math.ceil(estimation_sla_days) + self.sla_logistic = int(1) + self.sla_logistic_unit = 'hari' + self.sla_logistic_duration_unit = '1 hari' + else: + self.unlink() + else: + self.unlink() + - query = [ - ('product_id', '=', product.id), - ('picking_id', '!=', False), - ('picking_id.location_id', '=', 57), - ('picking_id.state', 'not in', ['cancel']) - ] - picking = self.env['stock.move.line'].search(query) - leadtimes=[] - for stock in picking: - date_delivered = stock.picking_id.driver_departure_date - date_so_confirmed = stock.picking_id.sale_id.date_order - if date_delivered and date_so_confirmed: - leadtime = date_delivered - date_so_confirmed - leadtime_in_days = leadtime.days - leadtimes.append(leadtime_in_days) + # query = [ + # ('product_id', '=', product.id), + # ('picking_id', '!=', False), + # ('picking_id.location_id', '=', 57), + # ('picking_id.state', 'not in', ['cancel']) + # ] + # picking = self.env['stock.move.line'].search(query) + + # leadtimes=[] + # for stock in picking: + # date_delivered = stock.picking_id.driver_departure_date + # date_do_ready = stock.picking_id.date_reserved + # if date_delivered and date_do_ready: + # leadtime = date_delivered - date_do_ready + # leadtime_in_days = leadtime.days + # leadtimes.append(leadtime_in_days) - if len(leadtimes) > 0: - avg_leadtime = sum(leadtimes) / len(leadtimes) - rounded_leadtime = math.ceil(avg_leadtime) - self.avg_leadtime = rounded_leadtime - if rounded_leadtime >= 1 and rounded_leadtime <= 5: - self.sla = '3-7 Hari' - elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - self.sla = '4-12 Hari' - elif rounded_leadtime >= 11: - self.sla = 'Indent' \ No newline at end of file + # if len(leadtimes) > 0: + # avg_leadtime = sum(leadtimes) / len(leadtimes) + # rounded_leadtime = math.ceil(avg_leadtime) + # estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 + # estimation_sla_days = estimation_sla / (24 * 60) + # self.sla = math.ceil(estimation_sla_days) + # self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index efacb95f..600dd90e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -422,6 +422,18 @@ class ProductProduct(models.Model): merchandise_ok = fields.Boolean(string='Product Promotion') qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + def generate_product_sla(self): + product_variant_ids = self.env.context.get('active_ids', []) + product_variant = self.search([('id', 'in', product_variant_ids)]) + sla_record = self.env['product.sla'].search([('product_variant_id', '=', product_variant.id)], limit=1) + + if sla_record: + sla_record.generate_product_sla() + else: + new_sla_record = self.env['product.sla'].create({ + 'product_variant_id': product_variant.id, + }) + new_sla_record.generate_product_sla() @api.model def create(self, vals): group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py new file mode 100644 index 00000000..851d9080 --- /dev/null +++ b/indoteknik_custom/models/public_holiday.py @@ -0,0 +1,11 @@ +from odoo import api, fields, models +from datetime import timedelta, datetime + +class PublicHoliday(models.Model): + _name = 'hr.public.holiday' + _description = 'Public Holidays' + + name = fields.Char(string='Holiday Name', required=True) + start_date = fields.Date('Date Holiday', required=True) + # end_date = fields.Date('End Holiday Date', required=True) + # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8a983479..037fff7b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -137,11 +137,13 @@ class SaleOrder(models.Model): applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) amount_voucher_shipping_disc = fields.Float(string='Voucher Discount') source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True) - estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0) + estimated_arrival_days = fields.Integer('Estimated Arrival To', default=0) + estimated_arrival_days_start = fields.Integer('Estimated Arrival From', default=0) email = fields.Char(string='Email') picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') - eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_date') + eta_date = fields.Datetime(string='ETA Date end', 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([ @@ -188,7 +190,17 @@ class SaleOrder(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) + estimated_ready_ship_date = fields.Datetime( + string='ET Ready to Ship compute', + compute='_compute_etrts_date' + ) + expected_ready_to_ship = fields.Datetime( + string='ET Ready to Ship', + copy=False, + store=True + ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') + reason_cancel = fields.Selection([ ('harga_terlalu_mahal', 'Harga barang terlalu mahal'), ('harga_web_tidak_valid', 'Harga web tidak valid'), @@ -435,19 +447,75 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True + @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): - max_leadtime = 0 + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) + else: + rec.eta_date = False + rec.eta_date_start = False + + + def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + holiday = self.env['hr.public.holiday'] + + while True : + today += timedelta(days=1) + offset += 1 + + if today.weekday() >= 5: + continue - for line in self.order_line: - leadtime = line.vendor_id.leadtime - max_leadtime = max(max_leadtime, leadtime) + is_holiday = holiday.search([("start_date", "=", today)]) + if is_holiday: + continue + + break + + return offset + @api.depends("order_line.product_id") + def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: - if rec.date_order and rec.state not in ['cancel', 'draft']: - eta_date = datetime.now() + timedelta(days=max_leadtime) - rec.eta_date = eta_date - else: - rec.eta_date = False + max_slatime = 1 # Default SLA jika tidak ada + for line in rec.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + max_slatime = max(max_slatime, slatime) + + if rec.date_order: + eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) + rec.estimated_ready_ship_date = eta_date + rec.commitment_date = eta_date + # Jika expected_ready_to_ship kosong, set nilai default + if not rec.expected_ready_to_ship: + rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + rec.commitment_date = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) + + def _set_etrts_date(self): + for order in self: + if order.state in ('done', 'cancel', 'sale'): + raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) + # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) def _prepare_invoice(self): """ @@ -642,15 +710,16 @@ class SaleOrder(models.Model): if line.product_id.type == 'product': line_no += 1 line.line_no = line_no + def write(self, vals): res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id - + return res def calculate_so_status(self): @@ -1108,6 +1177,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() + order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() @@ -1503,13 +1573,14 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) + order._compute_etrts_date() # order._update_partner_details() return order def write(self, vals): # Call the super method to handle the write operation res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() # Check if the update is coming from a save operation # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): # self._update_partner_details() @@ -1544,4 +1615,6 @@ class SaleOrder(models.Model): raise UserError( "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") res = super(SaleOrder, self).write(vals) + if 'order_line' in vals: + self._compute_etrts_date() return res \ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 36d9f63d..17ef1228 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ 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 timedelta, datetime +from datetime import timedelta, datetime as waktu from itertools import groupby import pytz, requests, json, requests from dateutil import parser @@ -12,10 +12,19 @@ import base64 import requests import time import logging +import re +from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) +_biteship_url = "https://api.biteship.com/v1" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" + + + class StockPicking(models.Model): _inherit = 'stock.picking' + _order = 'final_seq ASC' + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') @@ -166,6 +175,68 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + # Biteship Section + biteship_id = fields.Char(string="Biteship Respon ID") + biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") + biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + final_seq = fields.Float(string='Sequance Order', index=True) + + + def schduled_update_sequance(self): + query = "SELECT update_sequance_stock_picking();" + self.env.cr.execute(query) + + + @api.depends('estimated_ready_ship_date', 'state') + def _callculate_sequance(self): + for record in self: + try : + if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + rts = record.estimated_ready_ship_date - waktu.now() + rts_days = rts.days + rts_hours = divmod(rts.seconds, 3600) + + estimated_by_erts = rts.total_seconds() / 3600 + + record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + record.countdown_hours = estimated_by_erts + else: + record.countdown_hours = 999999999999 + record.countdown_ready_to_ship = False + except Exception as e : + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + print(str(e)) + return { 'error': str(e) } + + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_ready_to_ship(self): + # for record in self: + # if record.state in ('cancel', 'done'): + # record.countdown_ready_to_ship = False + # else: + # if record.estimated_ready_ship_date: + # delta = record.estimated_ready_ship_date - waktu.now() + # days = delta.days + # hours, remainder = divmod(delta.seconds, 3600) + # record.countdown_ready_to_ship = f"{days} days, {hours} hours" + # record.countdown_hours = delta.total_seconds() / 3600 + # else: + # record.countdown_ready_to_ship = False + def _compute_lalamove_image_html(self): for record in self: if record.lalamove_image_url: @@ -321,7 +392,7 @@ class StockPicking(models.Model): 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 @@ -332,12 +403,13 @@ class StockPicking(models.Model): 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" - + + if self.biteship_tracking_id: + raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") + # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - + # Fungsi untuk membangun items_data dari order lines def build_items_data(lines): return [{ @@ -370,6 +442,7 @@ class StockPicking(models.Model): }) payload = { + "reference_id " : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -381,7 +454,8 @@ class StockPicking(models.Model): "destination_contact_phone": self.real_shipping_id.phone or self.real_shipping_id.mobile, "destination_address": self.real_shipping_id.street, "destination_postal_code": self.real_shipping_id.zip, - "courier_type": "reg", + "origin_note": "BELAKANG INDOMARET", + "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", "destination_postal_code": self.real_shipping_id.zip, @@ -389,27 +463,42 @@ class StockPicking(models.Model): } # Cek jika pengiriman instant atau same_day - if "instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type: + if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "origin_note": "BELAKANG INDOMARET", - "courier_company": self.carrier_id.name.lower(), - "courier_type": self.sale_id.delivery_service_type, - "delivery_type": "now", - "items": items_data_instant # Gunakan items untuk instant + "origin_coordinate" :{ + "latitude": -6.3031123, + "longitude" : 106.7794934999 + }, + "destination_coordinate" : { + "latitude": self.real_shipping_id.latitude, + "longitude": self.real_shipping_id.longtitude, + }, + "items": items_data_instant }) - + + api_key = _biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } # Kirim request ke Biteship - response = requests.post(url, headers=headers, json=payload) + response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) + + if response.status_code == 200: + data = response.json() + + self.biteship_id = data.get("id", "") + self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") + self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") + self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") - if response.status_code == 201: - return response.json() + return data else: - raise UserError(f"Error saat mengirim ke Biteship: {response.content}") + error_data = response.json() + error_message = error_data.get("error", "Unknown error") + error_code = error_data.get("code", "No code provided") + raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): @@ -1039,11 +1128,14 @@ class StockPicking(models.Model): def get_tracking_detail(self): self.ensure_one() + + order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) response = { 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '', + 'service' : order.delivery_service_type or '', 'receiver_name': '', 'receiver_city': '' }, @@ -1052,8 +1144,21 @@ class StockPicking(models.Model): 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, 'eta': self.generate_eta_delivery(), + 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } + + if self.biteship_id : + histori = self.get_manifest_biteship() + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start) + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days) + formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" + response['eta'] = formatted_eta + response['manifests'] = histori.get("manifests", []) + response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False + response['status'] = self._map_status_biteship(histori.get("delivered")) + + return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False @@ -1066,6 +1171,77 @@ class StockPicking(models.Model): return response + def get_manifest_biteship(self): + api_key = _biteship_api_key + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + + manifests = [] + + try: + # Kirim request ke Biteship + response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) + result = response.json() + description = { + 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', + 'allocated' : 'Kurir akan melakukan pick-up pesanan', + 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("name", ""), + 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', + 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + 'delivered' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") + } + if(result.get('success') == True): + history = result.get("history", []) + status = result.get("status", "") + + for entry in reversed(history): + manifests.append({ + "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), + "datetime": self._convert_to_local_time(entry["updated_at"]), + # "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + "description": description[entry["status"]], + }) + + return { + "manifests": manifests, + "delivered": status + } + + return manifests + except Exception as e : + _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") + return { 'error': str(e) } + + def _convert_to_local_time(self, iso_date): + try: + dt_with_tz = waktu.fromisoformat(iso_date) + utc_dt = dt_with_tz.astimezone(pytz.utc) + + local_tz = pytz.timezone("Asia/Jakarta") + local_dt = utc_dt.astimezone(local_tz) + + return local_dt.strftime("%Y-%m-%d %H:%M:%S") + except Exception as e: + return str(e) + + def _map_status_biteship(self, status): + status_mapping = { + "confirmed": "pending", + "scheduled": "pending", + "allocated": "pending", + "picking_up": "pending", + "picked": "shipment", + "cancelled": "cancelled", + "on_hold": "on_hold", + "dropping_off": "shipment", + "delivered": "completed" + } + return status_mapping.get(status, "Hubungi Admin") + def generate_eta_delivery(self): current_date = datetime.datetime.now() prepare_days = 3 diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py new file mode 100644 index 00000000..852baa7a --- /dev/null +++ b/indoteknik_custom/models/vendor_sla.py @@ -0,0 +1,98 @@ +from odoo import models, fields, api +import logging +import math +_logger = logging.getLogger(__name__) + +class VendorSLA(models.Model): + _name = 'vendor.sla' + _description = 'Vendor SLA' + _rec_name = 'id_vendor' + + id_vendor = fields.Many2one('res.partner', string='Name', domain="[('industry_id', '=', 46)]") + duration = fields.Integer(string='Duration', description='SLA Duration') + unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Time" + ) + duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") + + # pertama, lakukan group by vendor pada modul purchase.order + # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order + # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration + def generate_vendor_id_sla(self): + # Step 1: Group stock pickings by vendor based on operation type + stock_picking_env = self.env['stock.picking'] + stock_moves = stock_picking_env.read_group( + domain=[ + ('state', '=', 'done'), + ('location_id', '=', 4), # Partner Locations/Vendors + ('location_dest_id', '=', 57) # BU/Stock + ], + fields=['partner_id', 'date_done', 'scheduled_date'], + groupby=['partner_id'], + lazy=False + ) + + for group in stock_moves: + partner_id = group['partner_id'][0] + total_duration = 0 + count = 0 + + # Step 2: Calculate the average duration for each vendor + pos_for_vendor = stock_picking_env.search([ + ('partner_id', '=', partner_id), + ('state', '=', 'done'), + ]) + + for po in pos_for_vendor: + if po.date_done and po.purchase_id.date_approve: + date_of_transfer = fields.Datetime.to_datetime(po.date_done) + po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) + if date_of_transfer < po_confirmation_date: continue + duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours + total_duration += duration + count += 1 + + if count > 0: + average_duration = total_duration / count + + # Step 3: Update the duration field in the corresponding res.partner record + vendor_sla = self.search([('id_vendor', '=', partner_id)], limit=1) + + # Konversi jam ke hari jika diperlukan + if average_duration >= 24: + days = average_duration / 24 + if days - int(days) > 0.5: # Jika sisa lebih dari 0,5, bulatkan ke atas + days = int(days) + 1 + else: # Jika sisa 0,5 atau kurang, bulatkan ke bawah + days = int(days) + duration_to_save = days + unit_to_save = 'hari' + else: + duration_to_save = round(average_duration) + unit_to_save = 'jam' + + # Update atau create vendor SLA record + if vendor_sla: + vendor_sla.write({ + 'duration': duration_to_save, + 'unit': unit_to_save + }) + else: + self.create({ + 'id_vendor': partner_id, + 'duration': duration_to_save, + 'unit': unit_to_save + }) + _logger.info(f'Proses SLA untuk Vendor selesai dilakukan') + + @api.depends('duration', 'unit') + def _compute_duration_unit(self): + for record in self: + if record.duration and record.unit: + record.duration_unit = f"{record.duration} {record.unit}" + else: + record.duration_unit = "-" + + + \ No newline at end of file diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml index 71748e44..b214dc87 100644 --- a/indoteknik_custom/views/product_product.xml +++ b/indoteknik_custom/views/product_product.xml @@ -31,6 +31,14 @@ model.action_sync_to_solr() + + Generate Product SLA + + + code + model.generate_product_sla() + + Sync Variant To Solr: Solr Flag 2 diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 8b0e874b..9179730f 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,7 +6,9 @@ - + + + @@ -21,7 +23,8 @@ - + + diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml new file mode 100644 index 00000000..146c5b0b --- /dev/null +++ b/indoteknik_custom/views/public_holiday.xml @@ -0,0 +1,55 @@ + + + + + + hr.public.holiday access + + + + + + + + + + + hr.public.holiday.form + hr.public.holiday + +
+ + + + + + +
+
+
+ + + hr.public.holiday.tree + hr.public.holiday + + + + + + + + + Public Holidays + hr.public.holiday + tree,form + + + +
+
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 163330c5..ebee64b1 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -68,7 +68,10 @@ + + + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 50ea40bf..9d19b97c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,7 +7,8 @@ - create_date desc + final_seq asc + @@ -18,6 +19,9 @@ + + + diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml new file mode 100644 index 00000000..cf4425eb --- /dev/null +++ b/indoteknik_custom/views/vendor_sla.xml @@ -0,0 +1,42 @@ + + + + Vendor SLA + vendor.sla + tree,form + + + + Vendor SLA + vendor.sla + + + + + + + + + + Vendor SLA + vendor.sla + +
+ + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/indoteknik_custom/views/x_banner_category.xml b/indoteknik_custom/views/x_banner_category.xml index 11feb207..a83c4129 100755 --- a/indoteknik_custom/views/x_banner_category.xml +++ b/indoteknik_custom/views/x_banner_category.xml @@ -23,7 +23,7 @@ - + -- cgit v1.2.3 From 502d44ac6ecc8bbeeea35a205f326d0c56980934 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 14:43:09 +0700 Subject: update code --- indoteknik_api/models/res_partner.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py index 0e09fbc6..3a98f3bc 100644 --- a/indoteknik_api/models/res_partner.py +++ b/indoteknik_api/models/res_partner.py @@ -59,6 +59,7 @@ class ResPartner(models.Model): # Pengiriman 'PIC_tittle' : pengajuan_tempo.pic_tittle if pengajuan_tempo.pic_tittle else '', + 'PICBarangMobile' : pengajuan_tempo.pic_mobile if pengajuan_tempo.pic_mobile 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 '', @@ -67,6 +68,7 @@ class ResPartner(models.Model): '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_mobile' : pengajuan_tempo.invoice_pic_mobile if pengajuan_tempo.invoice_pic_mobile 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 '', @@ -82,6 +84,12 @@ class ResPartner(models.Model): '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, + 'dokumen_prosedur': + { + 'name': pengajuan_tempo.dokumen_prosedur.name, + 'base64': pengajuan_tempo.dokumen_prosedur.datas.decode('utf-8'), + 'format': pengajuan_tempo.dokumen_prosedur.mimetype, + } if pengajuan_tempo.dokumen_prosedur else '', 'supplier_ids': [ { 'id': supplier.id, -- cgit v1.2.3 From 0554da8389d1622dc91aafee74390de888db9a6f Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 22:03:13 +0700 Subject: update stock picing --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 17ef1228..89531b79 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -182,7 +182,7 @@ class StockPicking(models.Model): estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) - final_seq = fields.Float(string='Sequance Order', index=True) + final_seq = fields.Float(string='Remaining Time') def schduled_update_sequance(self): @@ -207,7 +207,7 @@ class StockPicking(models.Model): record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False except Exception as e : - _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + _logger.error(f"Error calculating sequance {record.id}: {str(e)}") print(str(e)) return { 'error': str(e) } -- cgit v1.2.3 From 4115129907c5525c4688f2e6a3c28e0f249025b2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 22:07:00 +0700 Subject: bug fix --- indoteknik_custom/models/stock_picking.py | 4 ++-- indoteknik_custom/views/stock_picking.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 89531b79..6e003d24 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -180,8 +180,8 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) - countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 9d19b97c..dadd5021 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,8 +20,8 @@ - - + -- cgit v1.2.3 From 222fbfd3f88b6acea279887f7a4aee249960f000 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:37:26 +0700 Subject: fixing bug prosuct sla indent --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 037fff7b..fc99da36 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,7 +484,7 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 max_slatime = max(max_slatime, slatime) if rec.date_order: -- cgit v1.2.3 From afa847523195b695ec73430f9d1bda5a2fbfda51 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:47:25 +0700 Subject: validasi product sla --- indoteknik_custom/models/sale_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index fc99da36..e1074f42 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,6 +484,8 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + if(product_sla == False): + continue slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 max_slatime = max(max_slatime, slatime) -- cgit v1.2.3 From d516d0a28fdf78d0135cd480537e2079e1f1a006 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:53:40 +0700 Subject: tambah validasi --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e1074f42..4304d889 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -486,7 +486,7 @@ class SaleOrder(models.Model): product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) if(product_sla == False): continue - slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 + slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 max_slatime = max(max_slatime, slatime) if rec.date_order: -- cgit v1.2.3 From 1abd80916c1ec843343dacf2cd3a359651e7cd43 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 09:20:32 +0700 Subject: comment relate field --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6e003d24..217f234e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,7 +179,7 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + # estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') -- cgit v1.2.3 From 3696d94c52234025390ef92ef8928bee34d0de5b Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 11:34:45 +0700 Subject: update code --- indoteknik_api/controllers/api_v1/partner.py | 9 +++++---- indoteknik_custom/models/user_pengajuan_tempo.py | 1 + .../models/user_pengajuan_tempo_request.py | 21 ++++++++++++++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 9bb015c4..1d19f03d 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -311,7 +311,7 @@ class Partner(controller.Controller): 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) - dokumen_prosedur = kw.get('dokumen_prosedur', False) + dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False params = self.get_request_params(kw, { @@ -405,11 +405,12 @@ class Partner(controller.Controller): # Prosedur Pengiriman if dokumen_prosedur: - mimetype, _ = mimetypes.guess_type(dokumen_prosedur['details']['name']) + dokumen_prosedur = json.loads(dokumen_prosedur) + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) mimetype = mimetype or 'application/octet-stream' - data = base64.b64decode(dokumen_prosedur['details']['base64']) + data = base64.b64decode(dokumen_prosedur['base64']) sppkp_attachment = request.env['ir.attachment'].create({ - 'name': dokumen_prosedur['details']['name'], + 'name': dokumen_prosedur['name'], 'type': 'binary', 'datas': base64.b64encode(data), 'res_model': 'user.pengajuan.tempo', diff --git a/indoteknik_custom/models/user_pengajuan_tempo.py b/indoteknik_custom/models/user_pengajuan_tempo.py index d10e7e81..0b3ab63d 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo.py +++ b/indoteknik_custom/models/user_pengajuan_tempo.py @@ -99,6 +99,7 @@ class UserPengajuanTempo(models.Model): 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") + dokumen_prosedur = fields.Many2many('ir.attachment', 'dokumen_prosedur_rel', string="Dokumen Prosedur", tracking=True) # Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers") diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 72580001..abcb6f2f 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -132,7 +132,14 @@ class UserPengajuanTempoRequest(models.Model): 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) - + dokumen_prosedur = fields.Many2many( + 'ir.attachment', + 'dokumen_prosedur_rel', + string="Dokumen Prosedur", + related='pengajuan_tempo_id.dokumen_prosedur', + readonly=False, + tracking=3 + ) #Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line',related='pengajuan_tempo_id.supplier_ids', string="Suppliers", readonly=False) @@ -299,7 +306,7 @@ class UserPengajuanTempoRequest(models.Model): 'invoice_pic_tittle','invoice_pic_mobile', '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') + 'is_same_address', 'is_same_address_street','dokumen_prosedur') def _onchange_related_fields_pengiriman(self): if self.pengajuan_tempo_id: # Perbarui nilai di pengajuan_tempo_id @@ -328,6 +335,7 @@ class UserPengajuanTempoRequest(models.Model): 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 + self.pengajuan_tempo_id.dokumen_prosedur = self.dokumen_prosedur @api.onchange('supplier_ids') def _onchange_supplier_ids(self): @@ -341,7 +349,6 @@ class UserPengajuanTempoRequest(models.Model): 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 @@ -513,6 +520,7 @@ class UserPengajuanTempoRequest(models.Model): { "type": "delivery", "name": self.pengajuan_tempo_id.pic_name, + "phone": self.pengajuan_tempo_id.pic_mobile, "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, @@ -523,6 +531,7 @@ class UserPengajuanTempoRequest(models.Model): { "type": "invoice", "name": self.pengajuan_tempo_id.invoice_pic, + "phone": self.pengajuan_tempo_id.invoice_pic_mobile, "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, @@ -587,6 +596,10 @@ class UserPengajuanTempoRequest(models.Model): 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 + self.user_company_id.dokumen_prosedur = self.pengajuan_tempo_id.dokumen_prosedur[0] if self.pengajuan_tempo_id.dokumen_prosedur else [] + if self.user_company_id.dokumen_prosedur: + self.user_company_id.message_post(body='Dokumen Prosedur', + attachment_ids=[self.user_company_id.dokumen_prosedur.id]) # Referensi self.user_company_id.supplier_ids = self.pengajuan_tempo_id.supplier_ids @@ -596,6 +609,8 @@ class UserPengajuanTempoRequest(models.Model): 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]) -- cgit v1.2.3 From a4670aeed9688a33f56c07fe65984664390382f2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 13:08:29 +0700 Subject: update code api edit address --- indoteknik_api/controllers/api_v1/partner.py | 68 +++++++++++++++------------- indoteknik_custom/models/sale_order.py | 4 +- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 307165b3..d28aa8b4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -64,37 +64,43 @@ class Partner(controller.Controller): @http.route(prefix + 'partner//address', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_address_by_id(self, **kw): - params = self.get_request_params(kw, { - 'id': ['required', 'number'], - 'type': ['default:other'], - 'name': ['required'], - 'email': ['required'], - 'mobile': ['required'], - 'phone': [''], - 'street': ['required'], - 'state_id': ['required', 'number', 'alias:state_id'], - 'city_id': ['required', 'number', 'alias:kota_id'], - 'district_id': ['number', 'alias:kecamatan_id'], - 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], - 'zip': ['required'], - 'longtitude': [], - 'latitude': [], - 'address_map': [], - 'alamat_lengkap_text': [] - }) + try: + params = self.get_request_params(kw, { + 'id': ['required', 'number'], + 'type': ['default:other'], + 'name': ['required'], + 'email': ['required'], + 'mobile': ['required'], + 'phone': [''], + 'street': ['required'], + 'state_id': ['required', 'number', 'alias:state_id'], + 'city_id': ['required', 'number', 'alias:kota_id'], + 'district_id': ['number', 'alias:kecamatan_id'], + 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], + 'zip': ['required'], + 'longitude': '', # Perbaikan dari longtitude ke longitude + 'latitude': '', + 'address_map': [], + 'alamat_lengkap_text': [] + }) - if not params['valid']: - return self.response(code=400, description=params) - - partner = request.env[self._name].search([('id', '=', params['value']['id'])], limit=1) - if not partner: - return self.response(code=404, description='User not found') - - partner.write(params['value']) + if not params['valid']: + return self.response(code=400, description=params) - return self.response({ - 'id': partner.id - }) + partner = request.env[self._name].sudo().search([('id', '=', params['value']['id'])], limit=1) + + if not partner: + return self.response(code=404, description='User not found') + + try: + partner.write(params['value']) + except Exception as e: + return self.response(code=500, description=f'Error writing partner data: {str(e)}') + + return self.response({'id': partner.id}) + + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') @http.route(prefix + 'partner/address', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() @@ -111,8 +117,8 @@ class Partner(controller.Controller): 'city_id': ['required', 'number', 'alias:kota_id'], 'district_id': ['number', 'alias:kecamatan_id'], 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], - 'longtitude': [], - 'latitude': [], + 'longtitude': '', + 'latitude': '', 'address_map': [], 'zip': ['required'] }) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4304d889..4f85027a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,8 +484,8 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - if(product_sla == False): - continue + # if(product_sla == False): + # continue slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 max_slatime = max(max_slatime, slatime) -- cgit v1.2.3 From 405fbb57b55a2c01b1997e083b09e10fe838dbe2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 13:43:57 +0700 Subject: udpate webhook --- indoteknik_api/controllers/api_v1/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 15aac3cd..55e07152 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -141,6 +141,9 @@ class StockPicking(controller.Controller): @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def udpate_status_from_bitehsip(self, **kw): try: + if not request.jsonrequest: + return "ok" + data = request.jsonrequest # Ambil data JSON dari request event = data.get('event') -- cgit v1.2.3 From e9e8fa207275f6dce9046324b18f6148977eba2e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 14:35:23 +0700 Subject: update --- indoteknik_api/controllers/api_v1/partner.py | 536 ++++++++++++++------------- 1 file changed, 272 insertions(+), 264 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 1d19f03d..74e2b0aa 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -303,291 +303,299 @@ class Partner(controller.Controller): @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) - dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False - - 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'], - 'PICBarangMobile': ['alias:pic_mobile'], - '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'], - 'invoicePicMobile': ['alias:invoice_pic_mobile'], - '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 + try: + 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) + dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False + + 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'], + 'PICBarangMobile': ['alias:pic_mobile'], + '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'], + 'invoicePicMobile': ['alias:invoice_pic_mobile'], + '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'], + }) - if not params['valid']: - return self.response(code=400, description=params) - if params['value']['portal']: - if params['value']['portal'] == 'ada': - params['value']['portal'] = True + # # 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: + try: + pengajuan_tempo.write(new_data) + except Exception as e: + return self.response(code=500, description=f'Error updating partner data: {str(e)}') 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 - - # Prosedur Pengiriman - if dokumen_prosedur: - dokumen_prosedur = json.loads(dokumen_prosedur) - mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) - mimetype = mimetype or 'application/octet-stream' - data = base64.b64decode(dokumen_prosedur['base64']) - sppkp_attachment = request.env['ir.attachment'].create({ - 'name': dokumen_prosedur['name'], - 'type': 'binary', - 'datas': base64.b64encode(data), - 'res_model': 'user.pengajuan.tempo', - 'res_id': pengajuan_tempo.id, - 'mimetype': mimetype - }) - pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) - pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.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, - }) + try: + pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data) + pengajuan_tempo.partner_id = user_id + except Exception as e: + return self.response(code=500, description=f'Error creating partner data: {str(e)}') + + if partner_id: + pengajuan_tempo.name_tempo = partner_id + + # Prosedur Pengiriman + if dokumen_prosedur: + dokumen_prosedur = json.loads(dokumen_prosedur) + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(dokumen_prosedur['base64']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': dokumen_prosedur['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.pengajuan.tempo', + 'res_id': pengajuan_tempo.id, + 'mimetype': mimetype + }) + pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.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) + 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 - }) + 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])] + 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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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'] == '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])] + 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]) + 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'}) + 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, + }) + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') - 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([ -- cgit v1.2.3 From ad19154ae49ec5bc1178006344baf104154167bf Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 6 Mar 2025 15:11:57 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 18 ++++++++++++++++++ indoteknik_custom/models/stock_picking_return.py | 13 ++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0699295f..327389cd 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -852,6 +852,12 @@ class StockPicking(models.Model): if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) + + if len(self.check_koli_lines) == 0 and 'BU/PICK/' in self.name: + raise UserError(_("Tidak ada koli! Harap periksa kembali.")) + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") % (self.total_koli, self.total_so_koli)) @@ -1405,6 +1411,18 @@ class ScanKoli(models.Model): compute="_compute_scan_koli_progress" ) + @api.constrains('picking_id', 'koli_id') + def _check_duplicate_koli(self): + for record in self: + if record.koli_id: + existing_koli = self.search([ + ('picking_id', '=', record.picking_id.id), + ('koli_id', '=', record.koli_id.id), + ('id', '!=', record.id) # Exclude current record + ]) + if existing_koli: + raise ValidationError(f"⚠️ Koli '{record.koli_id.display_name}' sudah discan untuk picking ini!") + def unlink(self): picking_ids = set(self.mapped('koli_id.picking_id.id')) # Ambil semua picking_id yang terpengaruh for scan in self: diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index d4347235..a683d80e 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -24,4 +24,15 @@ class ReturnPicking(models.TransientModel): # 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 + return res + +class ReturnPickingLine(models.TransientModel): + _inherit = 'stock.return.picking.line' + + @api.onchange('quantity') + def _onchange_quantity(self): + for rec in self: + qty_done = rec.move_id.quantity_done + + if rec.quantity > qty_done: + raise UserError(f"Quantity yang Anda masukkan tidak boleh melebihi quantity done yaitu: {qty_done}") \ No newline at end of file -- cgit v1.2.3 From 2ec2cdc415e01cebb11f838f29b1099f5fb1b818 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 6 Mar 2025 15:25:53 +0700 Subject: rounding other taxes --- indoteknik_custom/models/account_move.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 9aa0743b..45fdb8df 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -76,7 +76,8 @@ class AccountMove(models.Model): def compute_other_taxes(self): for rec in self: - rec.other_taxes = rec.other_subtotal * 0.12 + rec.other_taxes = round(rec.other_subtotal * 0.12, 2) + def compute_other_subtotal(self): for rec in self: -- cgit v1.2.3 From f8d121e96cf92222b61d41b9299e78c18165eef3 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 16:12:27 +0700 Subject: fix code --- indoteknik_api/controllers/api_v1/partner.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 74e2b0aa..5a245a49 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -312,8 +312,9 @@ class Partner(controller.Controller): 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) - dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False - + dokumen_prosedur = False + if kw.get('formDokumenProsedur') and kw.get('formDokumenProsedur') != 'false': + dokumen_prosedur = kw.get('formDokumenProsedur') params = self.get_request_params(kw, { # informasi perusahaan @@ -406,7 +407,10 @@ class Partner(controller.Controller): return self.response(code=500, description=f'Error creating partner data: {str(e)}') if partner_id: - pengajuan_tempo.name_tempo = partner_id + try: + pengajuan_tempo.name_tempo = partner_id + except Exception as e: + return self.response(code=500, description=f'Error updating partner data: {str(e)}') # Prosedur Pengiriman if dokumen_prosedur: @@ -458,8 +462,12 @@ class Partner(controller.Controller): 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)] + try: + category_ids = list(map(int, category_produk_ids.split(','))) + pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') + tukar_invoice_input = kw.get('tukarInvoiceInput') if tukar_invoice_input: @@ -589,6 +597,9 @@ class Partner(controller.Controller): 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) + if not pengajuan_tempo: + return self.response(code=500, description="Failed to create or update pengajuan_tempo") + return self.response({ 'id': pengajuan_tempo.id, 'user_id': user_id, -- cgit v1.2.3 From ebfd584b6a1d44f15ce49fc8a6f9496818d7f377 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 21:35:17 +0700 Subject: filter po indent --- indoteknik_custom/models/vendor_sla.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 852baa7a..b052e6cb 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -49,6 +49,10 @@ class VendorSLA(models.Model): date_of_transfer = fields.Datetime.to_datetime(po.date_done) po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) if date_of_transfer < po_confirmation_date: continue + + days_difference = (date_of_transfer - po_confirmation_date).days + if days_difference > 14: + continue duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours total_duration += duration count += 1 -- cgit v1.2.3 From 3b8b4ec20d523cedf00d0a343ffc244e0a43da58 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 7 Mar 2025 10:39:00 +0700 Subject: when change name(individu) then nama pajak change --- indoteknik_custom/models/res_partner.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 7e574a72..fa2f6a81 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -470,4 +470,9 @@ class ResPartner(models.Model): 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 + raise UserError("NITKU harus memiliki tepat 22 angka.") + + @api.onchange('name') + def _onchange_name(self): + if self.company_type == 'person': + self.nama_wajib_pajak = self.name \ No newline at end of file -- cgit v1.2.3 From c80a48e26b972b203229e128209bb8da89d5da57 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 10 Mar 2025 04:36:11 +0700 Subject: bugs fix rts date --- indoteknik_custom/models/sale_order.py | 64 +++++++++++++++++++++++++++---- indoteknik_custom/models/stock_picking.py | 36 ++++++++--------- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4f85027a..e8bba662 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -450,7 +450,7 @@ class SaleOrder(models.Model): @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): for rec in self: - if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days and rec.estimated_arrival_days_start: rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) else: @@ -477,26 +477,74 @@ class SaleOrder(models.Model): break return offset + + # def calculate_sla_by_vendor(self, products): + # slatime = 15 + # for line in products: + # product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + # slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 + + # return { + # 'slatime' : slatime + # } + + def calculate_sla_by_vendor(self, products): + product_ids = products.mapped('product_id.id') # Kumpulkan semua ID produk + include_instant = True # Default True, tetapi bisa menjadi False + + # Cek apakah SEMUA produk memiliki qty_free_bandengan >= qty_needed + all_fast_products = all(product.product_id.qty_free_bandengan >= product.product_uom_qty for product in products) + if all_fast_products: + return {'slatime': 1, 'include_instant': include_instant} + + + # Cari semua vendor pemenang untuk produk yang diberikan + vendors = self.env['purchase.pricelist'].search([ + ('product_id', 'in', product_ids), + ('is_winner', '=', True) + ]) + + max_slatime = 1 + + for vendor in vendors: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + slatime = 15 + if vendor_sla: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration * 60 + include_instant = True + + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + slatime = math.ceil(estimation_sla_days) + + max_slatime = max(max_slatime, slatime) + + return {'slatime': max_slatime, 'include_instant': include_instant} @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: max_slatime = 1 # Default SLA jika tidak ada - for line in rec.order_line: - product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - # if(product_sla == False): - # continue - slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 - max_slatime = max(max_slatime, slatime) + slatime = self.calculate_sla_by_vendor(rec.order_line) + max_slatime = max(max_slatime, slatime['slatime']) if rec.date_order: - eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) + sum_days = max_slatime + self.get_days_until_next_business_day(rec.date_order) - 1 + if not rec.estimated_arrival_days: + rec.estimated_arrival_days = sum_days + + eta_date = rec.date_order + timedelta(days=sum_days) rec.estimated_ready_ship_date = eta_date rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default if not rec.expected_ready_to_ship: rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship def _onchange_expected_ready_ship_date(self): for rec in self: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 217f234e..b8bdcd94 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -190,26 +190,26 @@ class StockPicking(models.Model): self.env.cr.execute(query) - @api.depends('estimated_ready_ship_date', 'state') - def _callculate_sequance(self): - for record in self: - try : - if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): - rts = record.estimated_ready_ship_date - waktu.now() - rts_days = rts.days - rts_hours = divmod(rts.seconds, 3600) + # @api.depends('estimated_ready_ship_date', 'state') + # def _callculate_sequance(self): + # for record in self: + # try : + # if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + # rts = record.estimated_ready_ship_date - waktu.now() + # rts_days = rts.days + # rts_hours = divmod(rts.seconds, 3600) - estimated_by_erts = rts.total_seconds() / 3600 + # estimated_by_erts = rts.total_seconds() / 3600 - record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" - record.countdown_hours = estimated_by_erts - else: - record.countdown_hours = 999999999999 - record.countdown_ready_to_ship = False - except Exception as e : - _logger.error(f"Error calculating sequance {record.id}: {str(e)}") - print(str(e)) - return { 'error': str(e) } + # record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + # record.countdown_hours = estimated_by_erts + # else: + # record.countdown_hours = 999999999999 + # record.countdown_ready_to_ship = False + # except Exception as e : + # _logger.error(f"Error calculating sequance {record.id}: {str(e)}") + # print(str(e)) + # return { 'error': str(e) } # @api.depends('estimated_ready_ship_date', 'state') -- cgit v1.2.3 From efdf162216dceff9b456b967d6ccd38da56fd1d5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 09:14:28 +0700 Subject: add reason cancel --- indoteknik_custom/models/sale_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e8bba662..aee19ec7 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -214,6 +214,7 @@ class SaleOrder(models.Model): ('dokumen_tidak_support', 'Indoteknik tidak bisa support document yang dibutuhkan (Ex: TKDN, COO, SNI)'), ('ganti_quotation', 'Ganti Quotation'), ('testing_internal', 'Testing Internal'), + ('revisi_data', 'Revisi Data'), ], string='Reason for Cancel', copy=False, index=True, tracking=3) attachment_bukti = fields.Many2one( 'ir.attachment', -- cgit v1.2.3 From 92b1ec69af1fb5b062307c7c7a00c3e0fb0e9e01 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 09:50:55 +0700 Subject: fix pengajuan tempo --- indoteknik_api/controllers/api_v1/partner.py | 2 +- indoteknik_custom/models/res_partner.py | 3 +++ indoteknik_custom/views/res_partner.xml | 1 + indoteknik_custom/views/user_pengajuan_tempo_request.xml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 5a245a49..69ec8e69 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -426,7 +426,7 @@ class Partner(controller.Controller): 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) - pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[sppkp_attachment.id]) pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 56c69f95..b5ed9ce2 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -75,6 +75,7 @@ class ResPartner(models.Model): zip_invoice = fields.Char(string="Zip") tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice') jadwal_bayar = fields.Char(string='Jadwal Pembayaran') + dokumen_prosedur = fields.Many2one('ir.attachment', string="Dokumen Pengiriman", tracking=3, readonly=True) 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') @@ -273,6 +274,7 @@ class ResPartner(models.Model): 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_prosedur'] = vals.get('dokumen_prosedur', self.dokumen_prosedur) 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) @@ -344,6 +346,7 @@ class ResPartner(models.Model): 'zip_invoice': vals.get('zip_invoice'), 'tukar_invoice': vals.get('tukar_invoice'), 'jadwal_bayar': vals.get('jadwal_bayar'), + 'dokumen_prosedur': vals.get('dokumen_prosedur'), 'dokumen_pengiriman': vals.get('dokumen_pengiriman'), 'dokumen_pengiriman_input': vals.get('dokumen_pengiriman_input'), 'dokumen_invoice': vals.get('dokumen_invoice'), diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index b87e616d..cb9fa3ac 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -152,6 +152,7 @@ + diff --git a/indoteknik_custom/views/user_pengajuan_tempo_request.xml b/indoteknik_custom/views/user_pengajuan_tempo_request.xml index 3ab00ed9..339ce8db 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo_request.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo_request.xml @@ -123,6 +123,7 @@ + -- cgit v1.2.3 From 8ded428f62eca10e114a72c50074b4d8afa36dc8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 10:09:24 +0700 Subject: fix tempo bug --- indoteknik_api/controllers/api_v1/partner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 3e14a1dd..ebd91210 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -424,7 +424,7 @@ class Partner(controller.Controller): mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) mimetype = mimetype or 'application/octet-stream' data = base64.b64decode(dokumen_prosedur['base64']) - sppkp_attachment = request.env['ir.attachment'].create({ + dokumen_prosedur_attachment = request.env['ir.attachment'].create({ 'name': dokumen_prosedur['name'], 'type': 'binary', 'datas': base64.b64encode(data), @@ -432,8 +432,8 @@ class Partner(controller.Controller): 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) - pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[sppkp_attachment.id]) - pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] + pengajuan_tempo.dokumen_prosedur = [(6, 0, [dokumen_prosedur_attachment.id])] + pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[dokumen_prosedur_attachment.id]) form_supplier_data = kw.get('formSupplier', False) -- cgit v1.2.3 From 447edfb87520d5d1475eeba43f1cd3425222cd4d Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 10 Mar 2025 10:19:18 +0700 Subject: udate final_seq --- indoteknik_custom/models/sale_order.py | 1 + indoteknik_custom/models/stock_picking.py | 1 + 2 files changed, 2 insertions(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e8bba662..498f2467 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -512,6 +512,7 @@ class SaleOrder(models.Model): if vendor_sla: if vendor_sla.unit == 'hari': vendor_duration = vendor_sla.duration * 24 * 60 + include_instant = False else : vendor_duration = vendor_sla.duration * 60 include_instant = True diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b8bdcd94..ab8109c7 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -934,6 +934,7 @@ class StockPicking(models.Model): self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + self.final_seq = 0 self.send_mail_bills() return res -- cgit v1.2.3 From ffcad6c52773063a05a91721b1203975a5a6359e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 10 Mar 2025 14:33:43 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 3 --- indoteknik_custom/security/ir.model.access.csv | 3 --- 2 files changed, 6 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7b70c2b9..c5b6387d 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -20,8 +20,6 @@ _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" - - class StockPicking(models.Model): _inherit = 'stock.picking' @@ -245,7 +243,6 @@ class StockPicking(models.Model): query = "SELECT update_sequance_stock_picking();" self.env.cr.execute(query) - @api.depends('estimated_ready_ship_date', 'state') def _callculate_sequance(self): for record in self: diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4e580d03..57e02363 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -158,13 +158,10 @@ access_scan_koli,access.scan.koli,model_scan_koli,,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 -<<<<<<< HEAD access_sales_order_koli,access.sales.order.koli,model_sales_order_koli,,1,1,1,1 access_stock_backorder_confirmation,access.stock.backorder.confirmation,model_stock_backorder_confirmation,,1,1,1,1 access_warning_modal_wizard,access.warning.modal.wizard,model_warning_modal_wizard,,1,1,1,1 -======= ->>>>>>> odoo-backup 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 -- cgit v1.2.3 From 685c07a85cdad186ec14ad7bf9b78d52b34f8cb5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 20:57:43 +0700 Subject: update code ke right branch --- indoteknik_custom/models/stock_inventory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index 12a891de..b7020285 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -29,10 +29,10 @@ class StockInventory(models.Model): """Menentukan nomor berdasarkan kategori Adjust-In atau Adjust-Out.""" name_upper = record.name.upper() if record.name else "" - if self.adjusment_type == 'out' or "ADJUST OUT" in name_upper or "ADJUST-OUT" in name_upper or "OUT" in name_upper: + if self.adjusment_type == 'out': last_number = self._get_last_sequence("ADJUST/OUT/") record.number = f"ADJUST/OUT/{last_number}" - elif self.adjusment_type == 'in' or "ADJUST IN" in name_upper or "ADJUST-IN" in name_upper or "IN" in name_upper: + elif self.adjusment_type == 'in': last_number = self._get_last_sequence("ADJUST/IN/") record.number = f"ADJUST/IN/{last_number}" else: -- cgit v1.2.3 From ba57391edba128d8f58243d0dfd0d16a38e7dfec Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 21:54:48 +0700 Subject: when duplikat adjust doc number dont create first --- indoteknik_custom/models/stock_inventory.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index b7020285..69cca5bc 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -54,6 +54,26 @@ class StockInventory(models.Model): @api.model def create(self, vals): + """Pastikan nomor hanya dibuat saat penyimpanan.""" + if 'adjusment_type' in vals and not vals.get('number'): + vals['number'] = False # Jangan buat number otomatis dulu + order = super(StockInventory, self).create(vals) - self._assign_number(order) + + if order.adjusment_type: + self._assign_number(order) # Generate number setelah save + return order + + def write(self, vals): + """Jika adjusment_type diubah, generate ulang nomor.""" + res = super(StockInventory, self).write(vals) + if 'adjusment_type' in vals: + for record in self: + self._assign_number(record) + return res + + def copy(self, default=None): + """Saat duplikasi, adjusment_type dikosongkan dan number tidak ikut terduplikasi.""" + default = dict(default or {}, adjusment_type=False, number=False) + return super(StockInventory, self).copy(default=default) -- cgit v1.2.3 From 56e42190cb5cf039ee3ad05068dc7fb94f72943d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 11 Mar 2025 09:26:23 +0700 Subject: cr alert duplicate name --- indoteknik_custom/models/res_partner.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index fe04535c..fd3a0514 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -127,8 +127,8 @@ class ResPartner(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) - email_finance = fields.Char(string='Email Finance') - email_sales = fields.Char(string='Email Sales') + email_finance = fields.Char(string='Email Finance Vendor') + email_sales = fields.Char(string='Email Sales Vendor') user_payment_terms_sales = fields.Many2one('res.users', string='Users Update Payment Terms') date_payment_terms_sales = fields.Datetime(string='Date Update Payment Terms') @@ -191,10 +191,10 @@ class ResPartner(models.Model): def _check_duplicate_name(self): for record in self: if record.name: - # Mencari partner lain yang memiliki nama sama (case-insensitive) existing_partner = self.env['res.partner'].search([ - ('id', '!=', record.id), # Hindari mencocokkan diri sendiri - ('name', '=', record.name) # Case-insensitive search + ('id', '!=', record.id), + ('name', '=', record.name), + ('email', '=', record.email) ], limit=1) if existing_partner: -- cgit v1.2.3 From 351580ed5c3266a7fc956eb77efa1d2d2d0c5d57 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 12 Mar 2025 13:28:29 +0700 Subject: cr estimate shipping price --- indoteknik_custom/models/sale_order.py | 59 +++++++++++++++++++------- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/sale_order.xml | 1 + 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 852e3cf0..14a8e688 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -51,6 +51,16 @@ class CancelReasonOrder(models.TransientModel): order.confirm_cancel_order() return {'type': 'ir.actions.act_window_close'} + +class ShippingOption(models.Model): + _name = "shipping.option" + _description = "Shipping Option" + + name = fields.Char(string="Option Name", required=True) + price = fields.Float(string="Price", required=True) + provider = fields.Char(string="Provider") + etd = fields.Char(string="Estimated Delivery Time") + sale_order_id = fields.Many2one('sale.order', string="Sale Order", ondelete="cascade") class SaleOrder(models.Model): _inherit = "sale.order" @@ -221,6 +231,12 @@ class SaleOrder(models.Model): string="Attachment Bukti Cancel", readonly=False, ) nomor_so_pengganti = fields.Char(string='Nomor SO Pengganti', copy=False, tracking=3) + shipping_option_id = fields.Many2one("shipping.option", string="Selected Shipping Option", domain="['|', ('sale_order_id', '=', False), ('sale_order_id', '=', id)]") + + @api.constrains('shipping_option_id') + def _check_shipping_option(self): + for rec in self: + rec.delivery_amt = rec.shipping_option_id.price def _compute_shipping_method_picking(self): for order in self: @@ -268,15 +284,26 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - + if total_weight < 10: total_weight = 10 + self.delivery_amt = total_weight * 3000 + + shipping_option = self.env["shipping.option"].create({ + "name": "Indoteknik Delivery", + "price": self.delivery_amt, + "provider": "Indoteknik", + "etd": "1-2 Hari", + "sale_order_id": self.id, + }) + self.shipping_option_id = shipping_option.id def action_estimate_shipping(self): if self.carrier_id.id in [1, 151]: self.action_indoteknik_estimate_shipping() return + total_weight = 0 missing_weight_products = [] @@ -294,33 +321,35 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - # Mendapatkan city_id berdasarkan nama kota - origin_city_name = self.warehouse_id.partner_id.kota_id.name destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id - if not destination_subsdistrict_id: - raise UserError("Gagal mendapatkan ID kota asal atau tujuan.") + raise UserError("Gagal mendapatkan ID kota tujuan.") result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id) if result: - estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value'] - self.delivery_amt = estimated_cost - - shipping_info = [] + shipping_options = [] for courier in result['rajaongkir']['results']: for cost_detail in courier['costs']: service = cost_detail['service'] description = cost_detail['description'] etd = cost_detail['cost'][0]['etd'] value = cost_detail['cost'][0]['value'] - shipping_info.append(f"Service: {service}, Description: {description}, ETD: {etd} hari, Cost: Rp {value}") + shipping_options.append((service, description, etd, value, courier['code'])) + + self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink() + + for service, description, etd, value, provider in shipping_options: + self.env["shipping.option"].create({ + "name": service, + "price": value, + "provider": provider, + "etd": etd, + "sale_order_id": self.id, + }) - log_message = "
".join(shipping_info) + self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id - description_ongkir = result['rajaongkir']['results'][0]['costs'][0]['description'] - etd_ongkir = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['etd'] - service_ongkir = result['rajaongkir']['results'][0]['costs'][0]['service'] - self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
Service: {service_ongkir}
Description: {description_ongkir}
ETD: {etd_ongkir}
Detail Lain:
{log_message}") + self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
Detail Lain:
{'
'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}") else: raise UserError("Gagal mendapatkan estimasi ongkir.") diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4d9d8cf7..4d0e51eb 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -167,3 +167,4 @@ access_barcoding_product_line,access.barcoding.product.line,model_barcoding_prod access_account_payment_register,access.account.payment.register,model_account_payment_register,,1,1,1,1 access_stock_inventory,access.stock.inventory,model_stock_inventory,,1,1,1,1 access_cancel_reason_order,cancel.reason.order,model_cancel_reason_order,,1,1,1,0 +access_shipping_option,shipping.option,model_shipping_option,,1,1,1,1 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index ebee64b1..0d190f37 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -100,6 +100,7 @@ + -- cgit v1.2.3 From 731a47bee9925d65afe45f870880e9a42104fe7f Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 13 Mar 2025 08:54:55 +0700 Subject: add grouped so number and invoice number on customer commision --- indoteknik_custom/models/commision.py | 15 +++++++++++++++ indoteknik_custom/views/customer_commision.xml | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 6920154a..0d31e954 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -157,7 +157,22 @@ class CustomerCommision(models.Model): ('pending', 'Pending'), ('payment', 'Payment'), ], string='Payment Status', copy=False, readonly=True, tracking=3, default='pending') + grouped_so_number = fields.Char(string='Group SO Number', compute='_compute_grouped_numbers') + grouped_invoice_number = fields.Char(string='Group Invoice Number', compute='_compute_grouped_numbers') + def _compute_grouped_numbers(self): + for rec in self: + so_numbers = set() + invoice_numbers = set() + + for line in rec.commision_lines: + if line.invoice_id: + if line.invoice_id.sale_id: + so_numbers.add(line.invoice_id.sale_id.name) + invoice_numbers.add(line.invoice_id.name) + + rec.grouped_so_number = ', '.join(sorted(so_numbers)) + rec.grouped_invoice_number = ', '.join(sorted(invoice_numbers)) # add status for type of commision, fee, rebate / cashback # include child or not? diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 51172b1c..bb1628bc 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -17,6 +17,8 @@ decoration-danger="payment_status == 'pending'" widget="badge"/> + +
@@ -62,6 +64,8 @@ + +
-- cgit v1.2.3 From 1ff9a57e2f7a7ecb3ba9321f7133f43e7009aa47 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 13 Mar 2025 09:47:27 +0700 Subject: back to longtitude --- indoteknik_api/controllers/api_v1/partner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index ebd91210..126fded4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -78,7 +78,7 @@ class Partner(controller.Controller): 'district_id': ['number', 'alias:kecamatan_id'], 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], 'zip': ['required'], - 'longitude': '', # Perbaikan dari longtitude ke longitude + 'longtitude': '', 'latitude': '', 'address_map': [], 'alamat_lengkap_text': [] -- cgit v1.2.3 From 2b1783368e7be632e18be524b5288713125e7902 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 13 Mar 2025 11:25:22 +0700 Subject: uat bitehsip --- indoteknik_api/controllers/api_v1/product.py | 5 +++- indoteknik_custom/models/sale_order.py | 34 ++++++++++++---------------- indoteknik_custom/models/stock_picking.py | 17 ++++++++++---- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 557215ea..ef2d8bf2 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -34,9 +34,12 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - @http.route(prefix + 'product/variants/sla', auth='public', methods=['GET', 'OPTIONS']) + @http.route(prefix + 'product/variants/sla', auth='public', methods=['POST', 'OPTIONS']) @controller.Controller.must_authorized() def get_product_template_sla_by_id(self, **kwargs): + params = kwargs + + body_params = kwargs.get('ids') if not body_params: diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 852e3cf0..b22ee2ee 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -498,7 +498,6 @@ class SaleOrder(models.Model): if all_fast_products: return {'slatime': 1, 'include_instant': include_instant} - # Cari semua vendor pemenang untuk produk yang diberikan vendors = self.env['purchase.pricelist'].search([ ('product_id', 'in', product_ids), @@ -532,18 +531,18 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada slatime = self.calculate_sla_by_vendor(rec.order_line) max_slatime = max(max_slatime, slatime['slatime']) + + + current_date = datetime.now().date() if rec.date_order: - sum_days = max_slatime + self.get_days_until_next_business_day(rec.date_order) - 1 - if not rec.estimated_arrival_days: - rec.estimated_arrival_days = sum_days - - eta_date = rec.date_order + timedelta(days=sum_days) + sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 + rec.estimated_arrival_days = sum_days + + eta_date = current_date + timedelta(days=sum_days) rec.estimated_ready_ship_date = eta_date rec.commitment_date = eta_date - # Jika expected_ready_to_ship kosong, set nilai default - if not rec.expected_ready_to_ship: - rec.expected_ready_to_ship = eta_date + rec.expected_ready_to_ship = eta_date @@ -765,14 +764,10 @@ class SaleOrder(models.Model): def write(self, vals): - res = super(SaleOrder, self).write(vals) - # self._compute_etrts_date() if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id - - return res def calculate_so_status(self): so_state = ['sale'] @@ -1229,7 +1224,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() - order._compute_etrts_date() + # order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() @@ -1629,15 +1624,15 @@ class SaleOrder(models.Model): # order._update_partner_details() return order - def write(self, vals): + # def write(self, vals): # Call the super method to handle the write operation - res = super(SaleOrder, self).write(vals) + # res = super(SaleOrder, self).write(vals) # self._compute_etrts_date() # Check if the update is coming from a save operation # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): # self._update_partner_details() - return res + # return res def _update_partner_details(self): for order in self: @@ -1666,7 +1661,8 @@ class SaleOrder(models.Model): 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) - if 'order_line' in vals: - self._compute_etrts_date() + if any(field in vals for field in ["order_line", "client_order_ref"]): + self._compute_etrts_date() return res \ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ab8109c7..edc9cc78 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -13,11 +13,11 @@ import requests import time import logging import re -from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" @@ -492,8 +492,18 @@ class StockPicking(models.Model): self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") + + waybill_id = data.get("courier", {}).get("waybill_id", "") + + message = f"✅ Berhasil Order ke Biteship! Resi: {waybill_id}" if waybill_id else "⚠️ Order berhasil, tetapi tidak ada nomor resi." - return data + return { + 'effect': { + 'fadeout': 'slow', # Efek menghilang perlahan + 'message': message, # Pesan sukses + 'type': 'rainbow_man', # Efek animasi lucu Odoo + } + } else: error_data = response.json() error_message = error_data.get("error", "Unknown error") @@ -1203,7 +1213,6 @@ class StockPicking(models.Model): manifests.append({ "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), "datetime": self._convert_to_local_time(entry["updated_at"]), - # "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), "description": description[entry["status"]], }) -- cgit v1.2.3 From c351c87e00120508b49a47178f7c27cdad187eaa Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 13 Mar 2025 13:29:42 +0700 Subject: cr get banner side by keyword --- indoteknik_api/controllers/api_v1/banner.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/banner.py b/indoteknik_api/controllers/api_v1/banner.py index 83d0dc14..aea8f0d9 100644 --- a/indoteknik_api/controllers/api_v1/banner.py +++ b/indoteknik_api/controllers/api_v1/banner.py @@ -15,7 +15,8 @@ class Banner(controller.Controller): limit = int(kw.get('limit', 0)) offset = int(kw.get('offset', 0)) order = kw.get('order', 'write_date DESC') - + keyword = kw.get('keyword') + query = [('x_status_banner', '=', 'tayang')] if type: query += [('x_banner_category.x_studio_field_KKVl4', '=', type)] @@ -25,9 +26,27 @@ class Banner(controller.Controller): if manufacture_id: query += [('x_relasi_manufacture', '=', int(manufacture_id))] - - banners = request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) - + + banner_kumpulan = [] + banner_ids = set() # Set untuk menyimpan ID banner agar tidak duplikat + + if keyword: + keyword_list = [word.strip() for word in keyword.split() if word.strip()] # Pisahkan berdasarkan spasi + + for word in keyword_list: + keyword_query = query + [('x_keyword_banner', 'ilike', word)] # Buat query baru dengan keyword + banners = request.env['x_banner.banner'].search(keyword_query, limit=limit, offset=offset, order=order) + + for banner in banners: + if banner.id not in banner_ids: # Pastikan tidak ada duplikasi + banner_kumpulan.append(banner) + banner_ids.add(banner.id) + + if not keyword: + banners = request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) + else: + banners = banner_kumpulan if banner_kumpulan else request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) + week_number = self.get_week_number_of_current_month() end_datas = [] -- cgit v1.2.3 From 0459c5c45d5b29aa63c00686bd500310c13e78bf Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 13 Mar 2025 15:36:08 +0700 Subject: update register not found bisnis --- indoteknik_api/controllers/api_v1/user.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py index c0974367..8523d90b 100644 --- a/indoteknik_api/controllers/api_v1/user.py +++ b/indoteknik_api/controllers/api_v1/user.py @@ -162,9 +162,7 @@ class User(controller.Controller): 'sel_groups_1_9_10': 9 } - user = request.env['res.users'].create(user_data) - user.partner_id.email = email - user.partner_id.mobile = phone + if type_acc == 'business' and business_name: # Eksekusi query SQL menggunakan Levenshtein distance @@ -182,7 +180,9 @@ class User(controller.Controller): if result and is_terdaftar: match_company_name = result[2] match_company_id = result[0] - + user = request.env['res.users'].create(user_data) + user.partner_id.email = email + user.partner_id.mobile = phone # Create a user company request request.env['user.company.request'].create({ 'user_id': user.partner_id.id, @@ -190,6 +190,9 @@ class User(controller.Controller): 'user_input': business_name }) else: + if not result and is_terdaftar: + response['reason'] = 'BISNIS_NOT_FOUND' + return self.response(response) if not nama_wajib_pajak and is_pkp == 'false': nama_wajib_pajak = business_name @@ -213,6 +216,10 @@ class User(controller.Controller): 'property_account_payable_id': 438, 'active': False, } + + user = request.env['res.users'].create(user_data) + user.partner_id.email = email + user.partner_id.mobile = phone new_company = request.env['res.partner'].create(new_company_data) request.env['user.company.request'].create({ 'user_id': user.partner_id.id, @@ -247,8 +254,10 @@ class User(controller.Controller): 'mimetype': sppkp_mimetype }) new_company.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) - if type_acc == 'individu': + user = request.env['res.users'].create(user_data) + user.partner_id.email = email + user.partner_id.mobile = phone user.partner_id.customer_type = 'nonpkp' user.partner_id.npwp = '00.000.000.0-000.000' user.partner_id.sppkp = '-' -- cgit v1.2.3 From 29a64fa64c72c4cb263806b099775a18124a5da7 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 14 Mar 2025 08:34:52 +0700 Subject: cr md bom --- indoteknik_custom/models/sale_order.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 14a8e688..b311abda 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1,3 +1,5 @@ +from re import search + from odoo import fields, models, api, _ from odoo.exceptions import UserError, ValidationError from datetime import datetime, timedelta @@ -978,8 +980,20 @@ class SaleOrder(models.Model): raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") if not order.user_id.active: raise UserError("Salesperson sudah tidak aktif, mohon diisi yang benar pada data SO dan Contact") - + + def check_product_bom(self): + for order in self: + for line in order.order_line: + if 'bom' in line.product_id.default_code: + search_bom = self.env['mrp.production'].search([('product_id', '=', line.id)], limit=1,) + if not search_bom: + raise UserError("Product BOM belum ada di manufacturing orders, silahkan hubungi MD") + else: + if search_bom.state != 'confirmed': + raise UserError("Product BOM belum di confirm di manufacturing orders, silahkan hubungi MD") + def sale_order_approve(self): + self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() if self.validate_different_vendor() and not self.vendor_approval: -- cgit v1.2.3 From b445c5dc3621f3c4ad9ca4968a66ca25e2a1f84d Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 14 Mar 2025 08:43:33 +0700 Subject: fix register --- indoteknik_api/controllers/api_v1/user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py index 8523d90b..b5b7e055 100644 --- a/indoteknik_api/controllers/api_v1/user.py +++ b/indoteknik_api/controllers/api_v1/user.py @@ -131,6 +131,7 @@ class User(controller.Controller): nama_wajib_pajak = kw.get('nama_wajib_pajak', False) is_pkp = kw.get('is_pkp') is_terdaftar = kw.get('is_terdaftar', False) + is_terdaftar = False if is_terdaftar == 'false' else is_terdaftar type_acc = kw.get('type_acc', 'individu') or 'individu' if not name or not email or not password: -- cgit v1.2.3 From 9acebb424ead07109438e46c4f96038c9f50fbec Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 14 Mar 2025 09:36:51 +0700 Subject: cr calculate Estimated Ready To Ship Date --- indoteknik_custom/models/sale_order.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 14a8e688..01aefe7a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -206,8 +206,7 @@ class SaleOrder(models.Model): ) expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship', - copy=False, - store=True + copy=False ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') @@ -555,7 +554,7 @@ class SaleOrder(models.Model): return {'slatime': max_slatime, 'include_instant': include_instant} - @api.depends("order_line.product_id") + # @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: max_slatime = 1 # Default SLA jika tidak ada @@ -1258,7 +1257,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() - order._compute_etrts_date() + # order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() -- cgit v1.2.3 From 72bf5f78f3503f2a14c3a329653d0726a14d93c8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 14 Mar 2025 10:08:50 +0700 Subject: update code --- indoteknik_custom/models/sale_order.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b311abda..aed0c1df 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -984,14 +984,14 @@ class SaleOrder(models.Model): def check_product_bom(self): for order in self: for line in order.order_line: - if 'bom' in line.product_id.default_code: - search_bom = self.env['mrp.production'].search([('product_id', '=', line.id)], limit=1,) - if not search_bom: - raise UserError("Product BOM belum ada di manufacturing orders, silahkan hubungi MD") + if 'bom' in line.product_id.default_code.lower() or 'bom-it' in line.name.lower(): + search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id)],order='name desc') + if search_bom: + confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed') + if not confirmed_bom: + raise UserError("Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") else: - if search_bom.state != 'confirmed': - raise UserError("Product BOM belum di confirm di manufacturing orders, silahkan hubungi MD") - + raise UserError("Product BOM belum di confirm di manufacturing orders, silahkan hubungi MD") def sale_order_approve(self): self.check_product_bom() self.check_credit_limit() -- cgit v1.2.3 From cba69ee6c06c3386bed68b537c56393ae2cba11f Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 17 Mar 2025 09:01:52 +0700 Subject: fix code bom --- indoteknik_custom/models/sale_order.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index aed0c1df..adc9bb98 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -991,7 +991,7 @@ class SaleOrder(models.Model): if not confirmed_bom: raise UserError("Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") else: - raise UserError("Product BOM belum di confirm di manufacturing orders, silahkan hubungi MD") + raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") def sale_order_approve(self): self.check_product_bom() self.check_credit_limit() @@ -1053,6 +1053,7 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan2' return self._create_approval_notification('Pimpinan') elif order._requires_approval_margin_manager(): + self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' @@ -1232,6 +1233,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: + order.check_product_bom() order.check_credit_limit() order.check_limit_so_to_invoice() if self.validate_different_vendor() and not self.vendor_approval: -- cgit v1.2.3 From e4d86ee7cb2fac5c09876b1aeefda04f27ebedd0 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 17 Mar 2025 09:03:51 +0700 Subject: biteship handle sla SO --- indoteknik_api/controllers/api_v1/product.py | 97 +++++++++++++------------ indoteknik_api/controllers/api_v1/sale_order.py | 2 + indoteknik_custom/models/sale_order.py | 36 ++++----- 3 files changed, 71 insertions(+), 64 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index ef2d8bf2..a88c3368 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -34,63 +34,66 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - @http.route(prefix + 'product/variants/sla', auth='public', methods=['POST', 'OPTIONS']) + @http.route(prefix + 'product/variants/sla', auth='public', methods=['POST', 'OPTIONS'] , csrf=False) @controller.Controller.must_authorized() def get_product_template_sla_by_id(self, **kwargs): - params = kwargs + raw_data = kwargs.get('products', '[]') + product_data = json.loads(raw_data) - body_params = kwargs.get('ids') + product_ids = [int(item["id"]) for item in product_data] + products = request.env['purchase.pricelist'].search([ + ('product_id', 'in', product_ids), + ('is_winner', '=', True) + ]) - if not body_params: - return self.response('Failed', code=400, description='id is required') - - ids = [int(id.strip()) for id in body_params.split(',') if id.strip().isdigit()] - - sla_duration = 0 - sla_unit = 'Hari' + start_date = datetime.today().date() + additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) include_instant = True - products = request.env['product.product'].search([('id', 'in', ids)]) - if len(products) < 1: - return self.response( - 'Failed', - code=400, - description='Produk Tidak Di Temukan.' - ) - product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) - if len(product_slas) < 1: - return self.response( - 'Failed', - code=400, - description='SLA Tidak Di Temukan.' - ) + if(len(products) != len(product_ids)): + products_data_params = {product["id"] : product for product in product_data } - # Mapping SLA untuk mempermudah lookup - sla_map = {sla.product_variant_id.id: sla for sla in product_slas} - - for product in products: - product_sla = sla_map.get(product.id) - if product_sla: - sla_duration = max(sla_duration, int(product_sla.sla)) - sla_unit = product_sla.sla_vendor_id.unit - if product.qty_free_bandengan < 1 : - if product_sla.sla_vendor_id.unit != 'jam': - include_instant = False - break - - start_date = datetime.today().date() - additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) + all_fast_products = all( + product.product_id.qty_free_bandengan >= products_data_params.get(product.product_id.id, {}).get("quantity", 0) + for product in products + ) - # Jika semua loop selesai tanpa include_instant menjadi False + if all_fast_products: + return self.response({ + 'include_instant': include_instant, + 'sla_duration': 1, + 'sla_additional_days': additional_days, + 'sla_total' : int(1) + int(additional_days), + 'sla_unit': 'Hari' + }) + + max_slatime = 1 + + for vendor in products: + vendor_sla = request.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + slatime = 15 + if vendor_sla: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + include_instant = False + else : + vendor_duration = vendor_sla.duration * 60 + include_instant = True + + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + slatime = math.ceil(estimation_sla_days) + + max_slatime = max(max_slatime, slatime) + return self.response({ - 'include_instant': include_instant, - 'sla_duration': sla_duration, - 'sla_additional_days': additional_days, - 'sla_total' : int(sla_duration) + int(additional_days), - 'sla_unit': 'Hari' if additional_days > 0 else sla_unit - } - ) + 'include_instant': include_instant, + 'sla_duration': max_slatime, + 'sla_additional_days': additional_days, + 'sla_total' : int(max_slatime) + int(additional_days), + 'sla_unit': 'Hari' + }) @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 6815bf6c..98b13cad 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -466,6 +466,8 @@ class SaleOrder(controller.Controller): 'program_line_id': cart['id'], 'quantity': cart['quantity'] }) + + sale_order._compute_etrts_date() request.env['sale.order.promotion'].create(promotions) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b22ee2ee..7ccc551b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -450,10 +450,11 @@ class SaleOrder(models.Model): @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): + current_date = datetime.now().date() for rec in self: if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days and rec.estimated_arrival_days_start: - rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) - rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) + rec.eta_date = current_date + timedelta(days=rec.estimated_arrival_days) + rec.eta_date_start = current_date + timedelta(days=rec.estimated_arrival_days_start) else: rec.eta_date = False rec.eta_date_start = False @@ -527,22 +528,23 @@ class SaleOrder(models.Model): @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date - for rec in self: - max_slatime = 1 # Default SLA jika tidak ada - slatime = self.calculate_sla_by_vendor(rec.order_line) - max_slatime = max(max_slatime, slatime['slatime']) - - - current_date = datetime.now().date() - - if rec.date_order: - sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 - rec.estimated_arrival_days = sum_days + if self.order_line: + for rec in self: + max_slatime = 1 # Default SLA jika tidak ada + slatime = self.calculate_sla_by_vendor(rec.order_line) + max_slatime = max(max_slatime, slatime['slatime']) - eta_date = current_date + timedelta(days=sum_days) - rec.estimated_ready_ship_date = eta_date - rec.commitment_date = eta_date - rec.expected_ready_to_ship = eta_date + current_date = datetime.now().date() + + if rec.date_order: + sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 + if rec.source_id.name != 'Website': + rec.estimated_arrival_days = sum_days + + eta_date = current_date + timedelta(days=sum_days) + rec.estimated_ready_ship_date = eta_date + rec.commitment_date = eta_date + rec.expected_ready_to_ship = eta_date -- cgit v1.2.3 From c263662a9dd388eb9b23ae6ab8d9cd3f2bffb3f8 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 18 Mar 2025 09:22:54 +0700 Subject: push --- indoteknik_custom/models/commision.py | 36 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 67cc5a62..a5518c0a 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -152,19 +152,21 @@ class CustomerCommision(models.Model): commision_lines = fields.One2many('customer.commision.line', 'customer_commision_id', string='Lines', auto_join=True) status = fields.Selection([ ('draft', 'Menunggu Approval Manager Sales'), - ('pengajuan1', 'Menunggu Approval Marketing'), - ('pengajuan2', 'Menunggu Approval Pimpinan'), - ('pengajuan3', 'Menunggu Approval Accounting'), - ('pengajuan4', 'Menunggu Approval Finnence'), + ('pengajuan1', 'Menunggu Approval Sales'), + ('pengajuan2', 'Menunggu Approval Marketing'), + ('pengajuan3', 'Menunggu Approval Pimpinan'), + ('pengajuan4', 'Menunggu Approval Accounting'), + ('pengajuan5', 'Menunggu Approval Finance'), ('approved', 'Approved'), ('reject', 'Rejected'), ], string='Status', copy=False, readonly=True, tracking=3, index=True, track_visibility='onchange',default='draft') last_status = fields.Selection([ ('draft', 'Menunggu Approval Manager Sales'), - ('pengajuan1', 'Menunggu Approval Marketing'), - ('pengajuan2', 'Menunggu Approval Pimpinan'), - ('pengajuan3', 'Menunggu Approval Accounting'), - ('pengajuan4', 'Menunggu Approval Finnence'), + ('pengajuan1', 'Menunggu Approval Sales'), + ('pengajuan2', 'Menunggu Approval Marketing'), + ('pengajuan3', 'Menunggu Approval Pimpinan'), + ('pengajuan4', 'Menunggu Approval Accounting'), + ('pengajuan5', 'Menunggu Approval Finance'), ('approved', 'Approved'), ('reject', 'Rejected'), ], string='Status') @@ -187,6 +189,7 @@ class CustomerCommision(models.Model): ], string='Payment Status', copy=False, readonly=True, tracking=3, default='pending') note_finnance = fields.Text('Notes Finnance') reason_reject = fields.Char(string='Reason Reaject', tracking=True, track_visibility='onchange') + approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') # add status for type of commision, fee, rebate / cashback @@ -250,19 +253,26 @@ class CustomerCommision(models.Model): result = super(CustomerCommision, self).create(vals) return result - def action_confirm_customer_commision(self):#add 2 step approval + def action_confirm_customer_commision(self): if not self.status or self.status == 'draft': self.status = 'pengajuan1' - elif self.status == 'pengajuan1' and self.env.user.id == 19: + elif self.status == 'pengajuan1' and self.env.user.is_sales_manager: self.status = 'pengajuan2' - elif self.status == 'pengajuan2' and self.env.user.is_leader: + self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + elif self.status == 'pengajuan2' and self.env.user.id == 19: self.status = 'pengajuan3' - elif self.status == 'pengajuan3' and self.env.user.id == 1272: + self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + elif self.status == 'pengajuan3' and self.env.user.is_leader: self.status = 'pengajuan4' - elif self.status == 'pengajuan4' and self.env.user.id == 23: + self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + elif self.status == 'pengajuan4' and self.env.user.id == 1272: + self.status = 'pengajuan5' + self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + elif self.status == 'pengajuan5' and self.env.user.id == 23: for line in self.commision_lines: line.invoice_id.is_customer_commision = True self.status = 'approved' + self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name else: raise UserError('Harus di approved oleh yang bersangkutan') return -- cgit v1.2.3 From 0f962b64b4c62e236d6096c594d7fbe8c49276d8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 11:50:08 +0700 Subject: md selish --- indoteknik_custom/models/stock_picking.py | 40 ++++++++++++++++++++++++++++++- indoteknik_custom/views/stock_picking.xml | 16 ++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ab8109c7..1e93da80 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -131,6 +131,16 @@ class StockPicking(models.Model): ('cancel', 'Cancelled'), ], string='Status Reserve', tracking=True, copy=False, help="The current state of the stock picking.") notee = fields.Text(string="Note") + state_approve_md = fields.Selection([ + ('waiting', 'Waiting For Approve by MD'), + ('pending', 'Pending (cari dulu barangnya)'), + ('done', 'Approve by MD'), + ], string='Approval MD Gudang Selisih', tracking=True, copy=False, help="The current state of the MD Approval transfer barang from gudang selisih.") + show_state_approve_md = fields.Boolean(compute="_compute_show_state_approve_md") + + def _compute_show_state_approve_md(self): + for record in self: + record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih" @api.model def _compute_dokumen_tanda_terima(self): @@ -881,8 +891,36 @@ class StockPicking(models.Model): qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) if line.qty_done > qty_onhand: raise UserError('Quantity Done melebihi Quantity Onhand') + def button_state_approve_md(self): + group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id + users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) + active_model = self.env.context.get('active_model') + if self.env.user.id in users_in_group.mapped('id'): + self.state_approve_md = 'done' + else: + raise UserError('Hanya MD yang bisa Approve') + + def button_state_pending_md(self): + group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id + users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) + active_model = self.env.context.get('active_model') + if self.env.user.id in users_in_group.mapped('id'): + self.state_approve_md = 'pending' + else: + raise UserError('Hanya MD yang bisa Approve') def button_validate(self): + group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id + users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) + active_model = self.env.context.get('active_model') + if self.location_id.id == 47 and self.env.user.id not in users_in_group.mapped('id') and self.state_approve_md != 'done': + self.state_approve_md = 'waiting' if self.state_approve_md != 'pending' else 'pending' + self.env.cr.commit() + raise UserError("Transfer dari gudang selisih harus di approve MD, Hubungi MD agar bisa di Validate") + else: + if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): + self.state_approve_md = 'done' + 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") @@ -907,7 +945,7 @@ class StockPicking(models.Model): if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver: raise UserError("Harus di Approve oleh Logistik") - + if self.location_dest_id.id == 47 and not self.env.user.is_purchasing_manager: raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara") diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index dadd5021..d6ec4d3b 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -19,7 +19,8 @@ - + + @@ -70,6 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> + @@ -97,6 +109,8 @@ + + -- cgit v1.2.3 From 3f456ca27eaf98e5396da75f18e8106688491a46 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 11:53:55 +0700 Subject: update chek product bom --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4d186c8d..9d7be55a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -983,7 +983,7 @@ class SaleOrder(models.Model): def check_product_bom(self): for order in self: for line in order.order_line: - if 'bom' in line.product_id.default_code.lower() or 'bom-it' in line.name.lower(): + if 'bom-it' in line.name or 'bom' in line.product_id.default_code: search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id)],order='name desc') if search_bom: confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed') -- cgit v1.2.3 From 4b3c012f617683cdcb85251ac2da30d40ea4e093 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 13:04:17 +0700 Subject: fix code --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 9d7be55a..4d632c71 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -983,7 +983,7 @@ class SaleOrder(models.Model): def check_product_bom(self): for order in self: for line in order.order_line: - if 'bom-it' in line.name or 'bom' in line.product_id.default_code: + if 'bom-it' in line.product_id.name or 'bom' in line.product_id.default_code if line.product_id.default_code else False: search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id)],order='name desc') if search_bom: confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed') -- cgit v1.2.3 From a06059e47feab3aa25c35652dfb83b8783273084 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 13:14:12 +0700 Subject: fix code check bom-it --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4d632c71..67434105 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -983,7 +983,7 @@ class SaleOrder(models.Model): def check_product_bom(self): for order in self: for line in order.order_line: - if 'bom-it' in line.product_id.name or 'bom' in line.product_id.default_code if line.product_id.default_code else False: + if 'bom-it' in line.name.lower() or 'bom' in line.product_id.default_code.lower() if line.product_id.default_code else False: search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id)],order='name desc') if search_bom: confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed') -- cgit v1.2.3 From 2b34ec96718d693d7a322939b23bda2fe64d8c80 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 18 Mar 2025 15:25:34 +0700 Subject: push --- indoteknik_custom/models/commision.py | 24 +++++++++++++++++++----- indoteknik_custom/models/stock_picking.py | 2 ++ indoteknik_custom/views/customer_commision.xml | 2 ++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 62545984..d6b170d2 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -2,6 +2,7 @@ from odoo import models, api, fields, _ from odoo.exceptions import UserError from datetime import datetime import logging +from terbilang import Terbilang _logger = logging.getLogger(__name__) @@ -151,8 +152,7 @@ class CustomerCommision(models.Model): notification = fields.Char(string='Notification') commision_lines = fields.One2many('customer.commision.line', 'customer_commision_id', string='Lines', auto_join=True) status = fields.Selection([ - ('draft', 'Menunggu Approval Manager Sales'), - ('pengajuan1', 'Menunggu Approval Sales'), + ('pengajuan1', 'Menunggu Approval Manager Sales'), ('pengajuan2', 'Menunggu Approval Marketing'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('pengajuan4', 'Menunggu Approval Accounting'), @@ -161,8 +161,7 @@ class CustomerCommision(models.Model): ('reject', 'Rejected'), ], string='Status', copy=False, readonly=True, tracking=3, index=True, track_visibility='onchange',default='draft') last_status = fields.Selection([ - ('draft', 'Menunggu Approval Manager Sales'), - ('pengajuan1', 'Menunggu Approval Sales'), + ('pengajuan1', 'Menunggu Approval Manager Sales'), ('pengajuan2', 'Menunggu Approval Marketing'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('pengajuan4', 'Menunggu Approval Accounting'), @@ -172,6 +171,7 @@ class CustomerCommision(models.Model): ], string='Status') commision_percent = fields.Float(string='Commision %', tracking=3) commision_amt = fields.Float(string='Commision Amount', tracking=3) + commision_amt_text = fields.Char(string='Commision Amount Text', compute='compute_delivery_amt_text') total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp') commision_type = fields.Selection([ ('fee', 'Fee'), @@ -194,13 +194,27 @@ class CustomerCommision(models.Model): grouped_so_number = fields.Char(string='Group SO Number', compute='_compute_grouped_numbers') grouped_invoice_number = fields.Char(string='Group Invoice Number', compute='_compute_grouped_numbers') + def compute_delivery_amt_text(self): + tb = Terbilang() + + for record in self: + res = '' + + try: + if record.commision_amt > 0: + tb.parse(int(record.commision_amt)) + res = tb.getresult().title() + record.commision_amt_text = res + ' Rupiah' + except: + record.commision_amt_text = res + def _compute_grouped_numbers(self): for rec in self: so_numbers = set() invoice_numbers = set() for line in rec.commision_lines: - if line.invoice_id: + if line.invoice_id: if line.invoice_id.sale_id: so_numbers.add(line.invoice_id.sale_id.name) invoice_numbers.add(line.invoice_id.name) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ab8109c7..fe8557c3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1011,6 +1011,8 @@ 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") + + res = super(StockPicking, self).action_cancel() return res diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 7cdf3117..1d3f48fc 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -79,8 +79,10 @@ + +
-- cgit v1.2.3 From e7a1a6a1fbbc7e74291471d2abc9487511a8a861 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 15:39:13 +0700 Subject: fix code md gudang selisih --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1e93da80..9ea9f2a2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -133,7 +133,7 @@ class StockPicking(models.Model): notee = fields.Text(string="Note") state_approve_md = fields.Selection([ ('waiting', 'Waiting For Approve by MD'), - ('pending', 'Pending (cari dulu barangnya)'), + ('pending', 'Pending (perlu koordinasi dengan MD)'), ('done', 'Approve by MD'), ], string='Approval MD Gudang Selisih', tracking=True, copy=False, help="The current state of the MD Approval transfer barang from gudang selisih.") show_state_approve_md = fields.Boolean(compute="_compute_show_state_approve_md") -- cgit v1.2.3 From 53501956695d6fcc881125cff69e8e5cc9ebf2f5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 21:05:37 +0700 Subject: fix code --- indoteknik_custom/views/stock_picking.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index d6ec4d3b..ef56eec9 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + -- cgit v1.2.3 From e3ee409885f0b5ec1f1c229b2acec5f8b752c750 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 21:08:09 +0700 Subject: fix code --- indoteknik_custom/views/stock_picking.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index ef56eec9..ce07888a 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - @@ -109,8 +109,8 @@ - - + + -- cgit v1.2.3 From 59dd3804fff70606c82db6a990973f396fe9fb0f Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 21:09:58 +0700 Subject: add xml --- indoteknik_custom/views/stock_picking.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index ce07888a..d6ec4d3b 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - - - - - + @@ -109,8 +109,8 @@ - - + + -- cgit v1.2.3 From a12ed493887c4499f733f450a5ef826ee85b086e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 21:13:57 +0700 Subject: back to code xml --- indoteknik_custom/views/stock_picking.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index d6ec4d3b..ce07888a 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - @@ -109,8 +109,8 @@ - - + + -- cgit v1.2.3 From 61cec7a1d3595f661ba0fa1551e94f05ab0f27a2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 22:10:20 +0700 Subject: fix code --- indoteknik_custom/views/stock_picking.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index ce07888a..687b2b82 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - - - - - + -- cgit v1.2.3 From f275ebcd3813e9ea7af4b61f8bb48d030ad0ccc0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 18 Mar 2025 22:12:44 +0700 Subject: back to code --- indoteknik_custom/views/stock_picking.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 687b2b82..ce07888a 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - -- cgit v1.2.3 From 4a7b5ebc82de37c6d2bde5e670066336256939d5 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 19 Mar 2025 09:55:02 +0700 Subject: cr reklas uang muka and permission button cancel stock picking --- indoteknik_custom/models/invoice_reklas.py | 6 ++++++ indoteknik_custom/models/stock_picking.py | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index f5bb5a25..d10d4c31 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -18,6 +18,12 @@ class InvoiceReklas(models.TransientModel): ('pembelian', 'Pembelian'), ], string='Reklas Tipe') + @api.onchange('reklas_type') + def _onchange_reklas_type(self): + if self.reklas_type == 'penjualan': + invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) + self.pay_amt = invoices.amount_total + def create_reklas(self): if not self.reklas_type: raise UserError('Reklas Tipe harus diisi') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ab8109c7..4229d33e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1008,9 +1008,12 @@ class StockPicking(models.Model): return True def action_cancel(self): - if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': + if not self.env.user.is_logistic_approver: if self.origin and 'Return of' in self.origin: raise UserError("Button ini hanya untuk Logistik") + + if not self.env.user.has_group('indoteknik_custom.group_role_it') and not self.env.user.has_group('indoteknik_custom.group_role_logistic'): + raise UserError("Button ini hanya untuk Logistik") res = super(StockPicking, self).action_cancel() return res -- cgit v1.2.3 From fc5defa647bcdd317dc2d4069432c2dcc1141344 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 19 Mar 2025 10:04:31 +0700 Subject: change mthode validation expected date --- indoteknik_custom/models/sale_order.py | 87 ++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 7ccc551b..e2755eba 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -190,10 +190,10 @@ class SaleOrder(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) - estimated_ready_ship_date = fields.Datetime( - string='ET Ready to Ship compute', - compute='_compute_etrts_date' - ) + # estimated_ready_ship_date = fields.Datetime( + # string='ET Ready to Ship compute', + # compute='_compute_etrts_date' + # ) expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship', copy=False, @@ -479,16 +479,6 @@ class SaleOrder(models.Model): break return offset - - # def calculate_sla_by_vendor(self, products): - # slatime = 15 - # for line in products: - # product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - # slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 - - # return { - # 'slatime' : slatime - # } def calculate_sla_by_vendor(self, products): product_ids = products.mapped('product_id.id') # Kumpulkan semua ID produk @@ -526,43 +516,55 @@ class SaleOrder(models.Model): return {'slatime': max_slatime, 'include_instant': include_instant} - @api.depends("order_line.product_id") + @api.depends("order_line.product_id", "date_order") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date - if self.order_line: - for rec in self: - max_slatime = 1 # Default SLA jika tidak ada - slatime = self.calculate_sla_by_vendor(rec.order_line) - max_slatime = max(max_slatime, slatime['slatime']) - - current_date = datetime.now().date() + for rec in self: + if not rec.date_order: + rec.expected_ready_to_ship = False + return + + current_date = datetime.now().date() + + max_slatime = 1 # Default SLA jika tidak ada + slatime = self.calculate_sla_by_vendor(rec.order_line) + max_slatime = max(max_slatime, slatime['slatime']) - if rec.date_order: - sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 - if rec.source_id.name != 'Website': - rec.estimated_arrival_days = sum_days - - eta_date = current_date + timedelta(days=sum_days) - rec.estimated_ready_ship_date = eta_date - rec.commitment_date = eta_date - rec.expected_ready_to_ship = eta_date - - - - @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship - def _onchange_expected_ready_ship_date(self): + sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 + if not rec.estimated_arrival_days: + rec.estimated_arrival_days = sum_days + + eta_date = current_date + timedelta(days=sum_days) + rec.commitment_date = eta_date + # Jika expected_ready_to_ship kosong, set nilai default + if not rec.expected_ready_to_ship: + rec.expected_ready_to_ship = eta_date + + def _validate_expected_ready_ship_date(self): for rec in self: - if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + if rec.expected_ready_to_ship and rec.commitment_date: + current_date = datetime.now().date() # Hanya membandingkan tanggal saja, tanpa jam expected_date = rec.expected_ready_to_ship.date() - estimated_date = rec.estimated_ready_ship_date.date() - if expected_date < estimated_date: - rec.expected_ready_to_ship = rec.estimated_ready_ship_date - rec.commitment_date = rec.estimated_ready_ship_date + max_slatime = 1 # Default SLA jika tidak ada + slatime = self.calculate_sla_by_vendor(rec.order_line) + max_slatime = max(max_slatime, slatime['slatime']) + sum_days = max_slatime + self.get_days_until_next_business_day(current_date) - 1 + eta_minimum = current_date + timedelta(days=sum_days) + + if expected_date < eta_minimum: + rec.expected_ready_to_ship = eta_minimum raise ValidationError( "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." - .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + .format(eta_minimum.strftime('%d-%m-%Y'), eta_minimum.strftime('%d-%m-%Y')) ) + else: + rec.commitment_date = rec.expected_ready_to_ship + + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + self._validate_expected_ready_ship_date() def _set_etrts_date(self): for order in self: @@ -1623,6 +1625,7 @@ class SaleOrder(models.Model): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) order._compute_etrts_date() + order._validate_expected_ready_ship_date() # order._update_partner_details() return order -- cgit v1.2.3 From 4c8ff729c027654870b3cf71015e5c7ecec28a7b Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 19 Mar 2025 10:32:24 +0700 Subject: expected rts --- indoteknik_custom/models/sale_order.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index acad7729..b17df045 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -545,9 +545,8 @@ class SaleOrder(models.Model): max_slatime = max(max_slatime, slatime) return {'slatime': max_slatime, 'include_instant': include_instant} - - @api.depends("order_line.product_id", "date_order") - def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date + + def _calculate_etrts_date(self): for rec in self: if not rec.date_order: rec.expected_ready_to_ship = False @@ -565,9 +564,12 @@ class SaleOrder(models.Model): eta_date = current_date + timedelta(days=sum_days) rec.commitment_date = eta_date - # Jika expected_ready_to_ship kosong, set nilai default - if not rec.expected_ready_to_ship: - rec.expected_ready_to_ship = eta_date + rec.expected_ready_to_ship = eta_date + + @api.depends("order_line.product_id", "date_order") + def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date + self._calculate_etrts_date() + def _validate_expected_ready_ship_date(self): for rec in self: @@ -1713,5 +1715,5 @@ class SaleOrder(models.Model): res = super(SaleOrder, self).write(vals) if any(field in vals for field in ["order_line", "client_order_ref"]): - self._compute_etrts_date() + self._calculate_etrts_date() return res \ No newline at end of file -- cgit v1.2.3 From 8a23309d00183fb66f1e8c57f0087439fdb999ec Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 19 Mar 2025 10:33:29 +0700 Subject: update renca banner --- indoteknik_api/controllers/api_v1/banner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/banner.py b/indoteknik_api/controllers/api_v1/banner.py index aea8f0d9..64a6167b 100644 --- a/indoteknik_api/controllers/api_v1/banner.py +++ b/indoteknik_api/controllers/api_v1/banner.py @@ -45,7 +45,7 @@ class Banner(controller.Controller): if not keyword: banners = request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) else: - banners = banner_kumpulan if banner_kumpulan else request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) + banners = banner_kumpulan if len(banner_kumpulan) > 0 else request.env['x_banner.banner'].search(query, limit=limit, offset=offset, order=order) week_number = self.get_week_number_of_current_month() -- cgit v1.2.3 From 1f324148f176bafc471a5948b8c5322a9b175ffa Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 19 Mar 2025 12:49:44 +0700 Subject: request iman --- indoteknik_custom/models/stock_picking.py | 8 ++++---- indoteknik_custom/views/stock_picking.xml | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 9e5fca66..23ddb47f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -136,11 +136,11 @@ class StockPicking(models.Model): ('pending', 'Pending (perlu koordinasi dengan MD)'), ('done', 'Approve by MD'), ], string='Approval MD Gudang Selisih', tracking=True, copy=False, help="The current state of the MD Approval transfer barang from gudang selisih.") - show_state_approve_md = fields.Boolean(compute="_compute_show_state_approve_md") + # show_state_approve_md = fields.Boolean(compute="_compute_show_state_approve_md") - def _compute_show_state_approve_md(self): - for record in self: - record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih" + # def _compute_show_state_approve_md(self): + # for record in self: + # record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih" @api.model def _compute_dokumen_tanda_terima(self): diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index ce07888a..72fdefa7 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -71,17 +71,17 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> - - - - - + @@ -110,7 +110,7 @@ - + -- cgit v1.2.3 From 99cf4f8d2a109f09049a52bccdb85dde6aec1081 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 19 Mar 2025 13:17:52 +0700 Subject: fix bug --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 23ddb47f..b8a83d5c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1056,7 +1056,7 @@ class StockPicking(models.Model): return True def action_cancel(self): - if not self.env.user.is_logistic_approver: + if not self.env.user.is_logistic_approver and (self.env.context.get('active_model') == 'stock.picking' or self.env.context.get('active_model') == 'stock.picking.type'): if self.origin and 'Return of' in self.origin: raise UserError("Button ini hanya untuk Logistik") -- cgit v1.2.3 From cc1759574f76b084a1ce44e1acf01ed20dcdd729 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 19 Mar 2025 13:34:25 +0700 Subject: fix bug button cancel stock picking --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b8a83d5c..6c6cbaa1 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1060,7 +1060,7 @@ class StockPicking(models.Model): if self.origin and 'Return of' in self.origin: raise UserError("Button ini hanya untuk Logistik") - if not self.env.user.has_group('indoteknik_custom.group_role_it') and not self.env.user.has_group('indoteknik_custom.group_role_logistic'): + if not self.env.user.has_group('indoteknik_custom.group_role_it') and not self.env.user.has_group('indoteknik_custom.group_role_logistic') and self.picking_type_code == 'outgoing': raise UserError("Button ini hanya untuk Logistik") res = super(StockPicking, self).action_cancel() -- cgit v1.2.3 From c48f3204c84cf7fca8da827178c4971370b324f8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 20 Mar 2025 10:16:37 +0700 Subject: update logic duplicatee contact --- indoteknik_custom/models/user_pengajuan_tempo_request.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index abcb6f2f..caa420e5 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -548,20 +548,7 @@ class UserPengajuanTempoRequest(models.Model): ('name', '=', contact_data['name']) ], limit=1) - if existing_contact: - # Pastikan tidak ada duplikasi nama dalam perusahaan yang sama - duplicate_check = self.env['res.partner'].search([ - ('name', '=', contact_data['name']), - ('id', '!=', existing_contact.id) # Hindari update yang menyebabkan duplikasi global - ], limit=1) - - if not duplicate_check: - # Perbarui hanya field yang tidak menyebabkan konflik - update_data = {k: v for k, v in contact_data.items() if k != 'name'} - existing_contact.write(update_data) - else: - raise UserError(f"Skipping update for {contact_data['name']} due to existing duplicate.") - else: + if not existing_contact: # Pastikan tidak ada partner lain dengan nama yang sama sebelum membuat baru duplicate_check = self.env['res.partner'].search([ ('name', '=', contact_data['name']) -- cgit v1.2.3 From 3ed91948307260a25efae332c6dae013d276fef5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 20 Mar 2025 15:05:01 +0700 Subject: add sale order post massage when confirm product bom --- indoteknik_custom/models/mrp_production.py | 17 +++++++++++++++-- indoteknik_custom/views/mrp_production.xml | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 54d90256..0bf98702 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -6,5 +6,18 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' desc = fields.Text(string='Description') - - \ No newline at end of file + sale_order = fields.Many2one('sale.order', string='Sale Order', required=True, copy=False) + + def action_confirm(self): + """Override action_confirm untuk mengirim pesan ke Sale Order jika state berubah menjadi 'confirmed'.""" + if self._name != 'mrp.production': + return super(MrpProduction, self).action_confirm() + + result = super(MrpProduction, self).action_confirm() + + for record in self: + if record.sale_order and record.state == 'confirmed': + message = _("Manufacturing order telah dibuat dengan nomor %s") % (record.name) + record.sale_order.message_post(body=message) + + return result \ No newline at end of file diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index f81d65e8..95f419f6 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -7,6 +7,7 @@ + @@ -18,6 +19,7 @@ + -- cgit v1.2.3 From beb653de0340d270f2d56dd7b7145c3552e91ab4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 24 Mar 2025 09:37:33 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 42 +++++++++++++++++++++++++- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/stock_picking.xml | 15 ++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index c5b6387d..c3febc02 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -23,9 +23,10 @@ _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l class StockPicking(models.Model): _inherit = 'stock.picking' + _order = 'final_seq ASC' + konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True) scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True) check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', auto_join=True) - _order = 'final_seq ASC' check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) @@ -1585,6 +1586,29 @@ class ScanKoli(models.Model): compute="_compute_scan_koli_progress" ) + @api.onchange('koli_id') + def _onchange_koli_compare_with_konfirm_koli(self): + if not self.koli_id: + return + + # Pastikan konfirm_koli_lines tidak kosong + if not self.picking_id.konfirm_koli_lines: + raise UserError(_('Konfirm Koli Harus Diisi!')) + + # Ambil origin picking dari koli yang dipilih + koli_picking = self.koli_id.picking_id._origin + + # Kumpulkan semua origin picking dari konfirm koli lines + konfirm_pick_ids = [ + line.pick_id._origin + for line in self.picking_id.konfirm_koli_lines + if line.pick_id + ] + + # Validasi apakah koli_picking ada dalam daftar konfirmasi + if koli_picking not in konfirm_pick_ids: + raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + @api.constrains('picking_id', 'koli_id') def _check_duplicate_koli(self): for record in self: @@ -1713,6 +1737,22 @@ class ScanKoli(models.Model): # return remaining_scans +class KonfirmKoli(models.Model): + _name = 'konfirm.koli' + _description = 'Konfirm Koli' + _order = 'picking_id, id' + _rec_name = 'pick_id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + pick_id = fields.Many2one('stock.picking', string='Pick') + class WarningModalWizard(models.TransientModel): _name = 'warning.modal.wizard' _description = 'Peringatan Koli Belum Diperiksa' diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 57e02363..d3905d41 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -155,6 +155,7 @@ access_vendor_sla,access.vendor_sla,model_vendor_sla,,1,1,1,1 access_check_product,access.check.product,model_check_product,,1,1,1,1 access_check_koli,access.check.koli,model_check_koli,,1,1,1,1 access_scan_koli,access.scan.koli,model_scan_koli,,1,1,1,1 +access_konfirm_koli,access.konfirm.koli,model_konfirm_koli,,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 diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 144ed820..67593b5b 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -208,7 +208,10 @@ - + + + + @@ -227,6 +230,16 @@ + + konfirm.koli.tree + konfirm.koli + + + + + + + check.koli.tree check.koli -- cgit v1.2.3 From 5825faf58e86681f62eeeaf783bb2ac0c01afbf7 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Mar 2025 09:52:35 +0700 Subject: cr name request by widya --- indoteknik_custom/models/user_pengajuan_tempo_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index caa420e5..565b0315 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -110,7 +110,7 @@ class UserPengajuanTempoRequest(models.Model): pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_tittle', store=True, readonly=False) pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_mobile', 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) + street_pengiriman = fields.Char(string="Alamat Pengiriman Barang", 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) @@ -119,7 +119,7 @@ class UserPengajuanTempoRequest(models.Model): invoice_pic_tittle = fields.Char(string='Tittle PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_tittle', store=True, readonly=False) invoice_pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_mobile', 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) + street_invoice = fields.Char(string="Alamat Pengiriman Invoice", 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) -- cgit v1.2.3 From 99626f917b032110fe12b9a0ee86c218c0367be1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 24 Mar 2025 15:34:56 +0700 Subject: push --- indoteknik_custom/models/mrp_production.py | 169 ++++++++++++++++++++++++- indoteknik_custom/models/stock_move.py | 14 ++ indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/mrp_production.xml | 30 +++++ 4 files changed, 212 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 54d90256..ed05de91 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -1,4 +1,7 @@ -from odoo import fields, models, api, _ +from odoo import models, fields, api, tools, _ +from datetime import datetime, timedelta +import math +import logging from odoo.exceptions import AccessError, UserError, ValidationError @@ -6,5 +9,167 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' desc = fields.Text(string='Description') + status = fields.Selection([('pending', 'Pending'), ('approved', 'Approved'), ('reject', 'Reject')], string='Status', default='pending', tracking=3) + production_purchase_match = fields.One2many('production.purchase.match', 'production_id', string='Purchase Matches', auto_join=True) + + def action_approve(self): + if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': + self.status = 'approved' + + def action_reject(self): + if self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and self.status == 'pending': + self.status = 'reject' + + def create_po_from_manufacturing(self): + if not self.status == 'approved': + raise UserError('Harus Di Approve oleh Merchandiser') + + if not self.move_raw_ids: + raise UserError('Tidak ada Lines, belum bisa create PO') + # if self.is_po: + # raise UserError('Sudah pernah di create PO') + + vendor_ids = self.env['stock.move'].read_group([ + ('raw_material_production_id', '=', self.id), + ('vendor_id', '!=', False) + ], fields=['vendor_id'], groupby=['vendor_id']) + + po_ids = [] + for vendor in vendor_ids: + result_po = self.create_po_by_vendor(vendor['vendor_id'][0]) + po_ids += result_po + return { + 'name': _('Purchase Order'), + 'view_mode': 'tree,form', + 'res_model': 'purchase.order', + 'target': 'current', + 'type': 'ir.actions.act_window', + 'domain': [('id', 'in', po_ids)], + } + + + def create_po_by_vendor(self, vendor_id): + current_time = datetime.now() + + PRODUCT_PER_PO = 20 + + stock_move = self.env['stock.move'] + + param_header = { + 'partner_id': vendor_id, + # 'partner_ref': self.sale_order_id.name, + 'currency_id': 12, + 'user_id': self.env.user.id, + 'company_id': 1, # indoteknik dotcom gemilang + 'picking_type_id': 28, # indoteknik bandengan receipts + 'date_order': current_time, + # 'sale_order_id': self.sale_order_id.id, + 'note_description': 'from Manufacturing Order' + } + + domain = [ + ('raw_material_production_id', '=', self.id), + ('vendor_id', '=', vendor_id), + ('state', 'in', ['waiting','confirmed','partially_available']) + ] + + products_len = stock_move.search_count(domain) + page = math.ceil(products_len / PRODUCT_PER_PO) + po_ids = [] + # i start from zero (0) + for i in range(page): + new_po = self.env['purchase.order'].create([param_header]) + new_po.name = new_po.name + "/MO/" + str(i + 1) + po_ids.append(new_po.id) + lines = stock_move.search( + domain, + offset=i * PRODUCT_PER_PO, + limit=PRODUCT_PER_PO + ) + tax = [22] + + for line in lines: + product = line.product_id + price, taxes, vendor = self._get_purchase_price(product) + + param_line = { + 'order_id' : new_po.id, + 'product_id': product.id, + 'product_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, + 'product_uom_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, + 'name': product.display_name, + 'price_unit': price, + 'taxes_id': [taxes], + } + new_po_line = self.env['purchase.order.line'].create([param_line]) + + self.env['production.purchase.match'].create([{ + 'production_id': self.id, + 'order_id': new_po.id + }]) + # self.is_po = True + + return po_ids + + def _get_purchase_price(self, product_id): + override_vendor = product_id.x_manufacture.override_vendor_id + if override_vendor: + query = [('product_id', '=', product_id.id), + ('vendor_id', '=', override_vendor.id)] + purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + return self._get_valid_purchase_price(purchase_price) + else: + purchase_price = self.env['purchase.pricelist'].search( + [('product_id', '=', product_id.id), + ('is_winner', '=', True)], + limit=1) + + return self._get_valid_purchase_price(purchase_price) + + def _get_valid_purchase_price(self, purchase_price): + current_time = datetime.now() + delta_time = current_time - timedelta(days=365) + # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') + + price = 0 + taxes = '' + vendor_id = '' + human_last_update = purchase_price.human_last_update or datetime.min + system_last_update = purchase_price.system_last_update or datetime.min + + if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id + vendor_id = purchase_price.vendor_id.id + if delta_time > human_last_update: + price = 0 + taxes = '' + vendor_id = '' + + if system_last_update > human_last_update: + if purchase_price.taxes_system_id.type_tax_use == 'purchase': + price = purchase_price.system_price + taxes = purchase_price.taxes_system_id.id + vendor_id = purchase_price.vendor_id.id + if delta_time > system_last_update: + price = 0 + taxes = '' + vendor_id = '' + + return price, taxes, vendor_id - \ No newline at end of file + +class ProductionPurchaseMatch(models.Model): + _name = 'production.purchase.match' + _order = 'production_id, id' + + production_id = fields.Many2one('mrp.production', string='Ref', required=True, ondelete='cascade', index=True, copy=False) + order_id = fields.Many2one('purchase.order', string='Purchase Order') + vendor = fields.Char(string='Vendor', compute='_compute_info_po') + total = fields.Float(string='Total', compute='_compute_info_po') + + def _compute_info_po(self): + for match in self: + match.vendor = match.order_id.partner_id.name + match.total = match.order_id.amount_total + diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 6b631713..b5fc782e 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -13,6 +13,20 @@ class StockMove(models.Model): ) qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') barcode = fields.Char(string='Barcode', related='product_id.barcode') + vendor_id = fields.Many2one('res.partner' ,string='Vendor') + + @api.onchange('product_id') + def onchange_product_to_fill_vendor(self): + if self.product_id: + if self.product_id.x_manufacture.override_vendor_id: + self.vendor_id = self.product_id.x_manufacture.override_vendor_id.id + else: + purchase_pricelist = self.env['purchase.pricelist'].search( + [('product_id', '=', product_id.id), + ('is_winner', '=', True)], + limit=1) + if purchase_pricelist: + self.vendor_id = purchase_pricelist.vendor_id.id def _compute_qr_code_variant(self): for rec in self: diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4d0e51eb..8e9a08f0 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -168,3 +168,4 @@ access_account_payment_register,access.account.payment.register,model_account_pa access_stock_inventory,access.stock.inventory,model_stock_inventory,,1,1,1,1 access_cancel_reason_order,cancel.reason.order,model_cancel_reason_order,,1,1,1,0 access_shipping_option,shipping.option,model_shipping_option,,1,1,1,1 +access_production_purchase_match,access.production.purchase.match,model_production_purchase_match,,1,1,1,1 diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index f81d65e8..a28cdff8 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -5,9 +5,27 @@ mrp.production + + + + + + + + + + + + @@ -21,4 +39,16 @@ + + + production.purchase.match.tree + production.purchase.match + + + + + + + + -- cgit v1.2.3 From d094b9634a6d32549655c99ff370e45fb568f11d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 25 Mar 2025 11:06:32 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index c3febc02..0e425f68 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -937,9 +937,12 @@ class StockPicking(models.Model): raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): + if len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) + if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) - + if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) @@ -1593,7 +1596,7 @@ class ScanKoli(models.Model): # Pastikan konfirm_koli_lines tidak kosong if not self.picking_id.konfirm_koli_lines: - raise UserError(_('Konfirm Koli Harus Diisi!')) + raise UserError(_('Mapping Koli Harus Diisi!')) # Ambil origin picking dari koli yang dipilih koli_picking = self.koli_id.picking_id._origin @@ -1607,7 +1610,7 @@ class ScanKoli(models.Model): # Validasi apakah koli_picking ada dalam daftar konfirmasi if koli_picking not in konfirm_pick_ids: - raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + raise UserError(_('Koli tidak sesuai dengan mapping koli, pastikan picking terkait benar!')) @api.constrains('picking_id', 'koli_id') def _check_duplicate_koli(self): -- cgit v1.2.3 From db3ff0677f9c6ddd0f04ebcf3e9c780045259f73 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 25 Mar 2025 13:48:16 +0700 Subject: fix get purchase pricelist --- indoteknik_custom/models/mrp_production.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index ed05de91..0e17fda9 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -98,8 +98,8 @@ class MrpProduction(models.Model): 'product_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, 'product_uom_qty': line.product_uom_qty if line.state in ['confirmed', 'waiting'] else line.product_uom_qty - line.forecast_availability, 'name': product.display_name, - 'price_unit': price, - 'taxes_id': [taxes], + 'price_unit': price if price else 0.0, + 'taxes_id': [taxes] if taxes else [], } new_po_line = self.env['purchase.order.line'].create([param_line]) @@ -113,10 +113,10 @@ class MrpProduction(models.Model): def _get_purchase_price(self, product_id): override_vendor = product_id.x_manufacture.override_vendor_id - if override_vendor: - query = [('product_id', '=', product_id.id), - ('vendor_id', '=', override_vendor.id)] - purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + query = [('product_id', '=', product_id.id), + ('vendor_id', '=', override_vendor.id)] + purchase_price = self.env['purchase.pricelist'].search(query, limit=1) + if purchase_price: return self._get_valid_purchase_price(purchase_price) else: purchase_price = self.env['purchase.pricelist'].search( -- cgit v1.2.3 From 459790b31fac1a789efcb946f26938b60b123aab Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 25 Mar 2025 15:15:05 +0700 Subject: add field date_approve and position --- indoteknik_custom/models/commision.py | 27 ++++++++++++++++++++------ indoteknik_custom/views/customer_commision.xml | 3 +-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index d6b170d2..f94d3c7a 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -156,7 +156,6 @@ class CustomerCommision(models.Model): ('pengajuan2', 'Menunggu Approval Marketing'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('pengajuan4', 'Menunggu Approval Accounting'), - ('pengajuan5', 'Menunggu Approval Finance'), ('approved', 'Approved'), ('reject', 'Rejected'), ], string='Status', copy=False, readonly=True, tracking=3, index=True, track_visibility='onchange',default='draft') @@ -165,7 +164,6 @@ class CustomerCommision(models.Model): ('pengajuan2', 'Menunggu Approval Marketing'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('pengajuan4', 'Menunggu Approval Accounting'), - ('pengajuan5', 'Menunggu Approval Finance'), ('approved', 'Approved'), ('reject', 'Rejected'), ], string='Status') @@ -194,6 +192,18 @@ class CustomerCommision(models.Model): grouped_so_number = fields.Char(string='Group SO Number', compute='_compute_grouped_numbers') grouped_invoice_number = fields.Char(string='Group Invoice Number', compute='_compute_grouped_numbers') + sales_id = fields.Many2one('res.users', string="Sales", tracking=True) + + date_approved_sales = fields.Datetime(string="Date Approved Sales", tracking=True) + date_approved_marketing = fields.Datetime(string="Date Approved Marketing", tracking=True) + date_approved_pimpinan = fields.Datetime(string="Date Approved Pimpinan", tracking=True) + date_approved_accounting = fields.Datetime(string="Date Approved Accounting", tracking=True) + + position_sales = fields.Char(string="Position Sales", tracking=True) + position_marketing = fields.Char(string="Position Marketing", tracking=True) + position_pimpinan = fields.Char(string="Position Pimpinan", tracking=True) + position_accounting = fields.Char(string="Position Accounting", tracking=True) + def compute_delivery_amt_text(self): tb = Terbilang() @@ -283,25 +293,31 @@ class CustomerCommision(models.Model): return result def action_confirm_customer_commision(self): + now = datetime.utcnow() if not self.status or self.status == 'draft': self.status = 'pengajuan1' elif self.status == 'pengajuan1' and self.env.user.is_sales_manager: self.status = 'pengajuan2' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + self.date_approved_sales = now + self.position_sales = 'Sales Manager' elif self.status == 'pengajuan2' and self.env.user.id == 19: self.status = 'pengajuan3' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + self.date_approved_marketing = now + self.position_marketing = 'Marketing Manager' elif self.status == 'pengajuan3' and self.env.user.is_leader: self.status = 'pengajuan4' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + self.date_approved_pimpinan = now + self.position_pimpinan = 'Pimpinan' elif self.status == 'pengajuan4' and self.env.user.id == 1272: - self.status = 'pengajuan5' - self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name - elif self.status == 'pengajuan5' and self.env.user.id == 23: for line in self.commision_lines: line.invoice_id.is_customer_commision = True self.status = 'approved' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name + self.date_approved_accounting = now + self.position_accounting = 'Accounting' else: raise UserError('Harus di approved oleh yang bersangkutan') return @@ -320,7 +336,6 @@ class CustomerCommision(models.Model): for commision in self: commision.status = commision.last_status if commision.last_status else 'draft' - def action_confirm_customer_payment(self): if self.status != 'approved': raise UserError('Commision harus di approve terlebih dahulu sebelum di konfirmasi pembayarannya') diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 1d3f48fc..a5f0e07f 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -55,8 +55,7 @@ - - - diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 36c0db13..d6ad2408 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -65,6 +65,7 @@ + diff --git a/indoteknik_custom/views/purchasing_job.xml b/indoteknik_custom/views/purchasing_job.xml index 16f1bedd..bb1c7643 100644 --- a/indoteknik_custom/views/purchasing_job.xml +++ b/indoteknik_custom/views/purchasing_job.xml @@ -17,6 +17,7 @@ + -- cgit v1.2.3 From b105f669873645f29314be55aac45d95d0556970 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 27 Mar 2025 16:20:05 +0700 Subject: fix bug --- indoteknik_custom/models/mrp_production.py | 7 ++++++- indoteknik_custom/models/stock_move.py | 21 +++++++++++---------- indoteknik_custom/views/mrp_production.xml | 3 ++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 1813dbeb..d80df2ce 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -11,6 +11,7 @@ class MrpProduction(models.Model): desc = fields.Text(string='Description') sale_order = fields.Many2one('sale.order', string='Sale Order', required=True, copy=False) production_purchase_match = fields.One2many('production.purchase.match', 'production_id', string='Purchase Matches', auto_join=True) + is_po = fields.Boolean(string='Is PO') def action_confirm(self): """Override action_confirm untuk mengirim pesan ke Sale Order jika state berubah menjadi 'confirmed'.""" @@ -31,6 +32,9 @@ class MrpProduction(models.Model): if not self.state == 'confirmed': raise UserError('Harus Di Approve oleh Merchandiser') + if self.is_po == True: + raise UserError('Sudah pernah di buat PO') + if not self.move_raw_ids: raise UserError('Tidak ada Lines, belum bisa create PO') # if self.is_po: @@ -115,7 +119,8 @@ class MrpProduction(models.Model): 'production_id': self.id, 'order_id': new_po.id }]) - # self.is_po = True + + self.is_po = True return po_ids diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 87b1c94e..514acad0 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -17,16 +17,17 @@ class StockMove(models.Model): @api.constrains('product_id') def constrains_product_to_fill_vendor(self): - if self.product_id: - if self.product_id.x_manufacture.override_vendor_id: - self.vendor_id = self.product_id.x_manufacture.override_vendor_id.id - else: - purchase_pricelist = self.env['purchase.pricelist'].search( - [('product_id', '=', product_id.id), - ('is_winner', '=', True)], - limit=1) - if purchase_pricelist: - self.vendor_id = purchase_pricelist.vendor_id.id + for rec in self: + if rec.product_id and rec.bom_line_id: + if rec.product_id.x_manufacture.override_vendor_id: + rec.vendor_id = rec.product_id.x_manufacture.override_vendor_id.id + else: + purchase_pricelist = self.env['purchase.pricelist'].search( + [('product_id', '=', rec.product_id.id), + ('is_winner', '=', True)], + limit=1) + if purchase_pricelist: + rec.vendor_id = purchase_pricelist.vendor_id.id def _compute_qr_code_variant(self): for rec in self: diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index ffbc8659..f8278f39 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -6,11 +6,12 @@ + -- cgit v1.2.3 From b9a81c1a9b495571a5cb30993a31eda7c5ab871f Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 7 Apr 2025 10:42:33 +0700 Subject: cr validation delivery amt --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6dd31d89..8d9af692 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -668,7 +668,7 @@ class SaleOrder(models.Model): if self.email and not re.match(pattern, self.email): raise UserError('Email yang anda input kurang valid') - @api.constrains('delivery_amt', 'carrier_id', 'shipping_cost_covered') + # @api.constrains('delivery_amt', 'carrier_id', 'shipping_cost_covered') def _validate_delivery_amt(self): if self.delivery_amt < 1: if(self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik') and not self.env.context.get('active_id', []): -- cgit v1.2.3 From ccd98307c3b48b25bbbb053caa2dba0cce5117d1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 9 Apr 2025 17:07:30 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 36 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0e425f68..be033b39 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -943,8 +943,8 @@ class StockPicking(models.Model): if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) - if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': - raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) + # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) if len(self.check_koli_lines) == 0 and 'BU/PICK/' in self.name: raise UserError(_("Tidak ada koli! Harap periksa kembali.")) @@ -1594,21 +1594,17 @@ class ScanKoli(models.Model): if not self.koli_id: return - # Pastikan konfirm_koli_lines tidak kosong if not self.picking_id.konfirm_koli_lines: raise UserError(_('Mapping Koli Harus Diisi!')) - # Ambil origin picking dari koli yang dipilih koli_picking = self.koli_id.picking_id._origin - # Kumpulkan semua origin picking dari konfirm koli lines konfirm_pick_ids = [ line.pick_id._origin for line in self.picking_id.konfirm_koli_lines if line.pick_id ] - # Validasi apakah koli_picking ada dalam daftar konfirmasi if koli_picking not in konfirm_pick_ids: raise UserError(_('Koli tidak sesuai dengan mapping koli, pastikan picking terkait benar!')) @@ -1619,20 +1615,18 @@ class ScanKoli(models.Model): existing_koli = self.search([ ('picking_id', '=', record.picking_id.id), ('koli_id', '=', record.koli_id.id), - ('id', '!=', record.id) # Exclude current record + ('id', '!=', record.id) ]) if existing_koli: raise ValidationError(f"⚠️ Koli '{record.koli_id.display_name}' sudah discan untuk picking ini!") def unlink(self): - picking_ids = set(self.mapped('koli_id.picking_id.id')) # Ambil semua picking_id yang terpengaruh + picking_ids = set(self.mapped('koli_id.picking_id.id')) for scan in self: koli = scan.koli_id.koli_id if koli: - # Hapus reserved_id saat scan dihapus koli.reserved_id = False - # Periksa ulang apakah masih ada scan.koli yang tersisa untuk setiap picking_id for picking_id in picking_ids: remaining_scans = self.env['sales.order.koli'].search_count([ ('koli_id.picking_id', '=', picking_id) @@ -1640,8 +1634,6 @@ class ScanKoli(models.Model): delete_koli = len(self.filtered(lambda rec: rec.koli_id.picking_id.id == picking_id)) - - # Jika tidak ada scan.koli lain yang tersisa, set linked_out_picking_id ke False if remaining_scans == delete_koli: picking = self.env['stock.picking'].browse(picking_id) picking.linked_out_picking_id = False @@ -1666,7 +1658,6 @@ class ScanKoli(models.Model): scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin def _compute_scan_koli_progress(self): - """ Menghitung progres scan koli dalam format 'X/Y' """ for scan in self: if scan.picking_id: all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') @@ -1676,7 +1667,6 @@ class ScanKoli(models.Model): @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): - """ Validasi jika jumlah scan koli melebihi total SO koli """ for scan in self.picking_id.scan_koli_lines: scan.koli_id.koli_id.reserved_id = scan.picking_id.id scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id @@ -1709,24 +1699,21 @@ class ScanKoli(models.Model): koli_count_by_picking = defaultdict(int) for scan in self: - koli_count_by_picking[scan.koli_id.picking_id.id] += 1 # Hitung jumlah koli per picking + koli_count_by_picking[scan.koli_id.picking_id.id] += 1 for picking_id, total_koli in koli_count_by_picking.items(): picking = self.env['stock.picking'].browse(picking_id) if total_koli == picking.quantity_koli: - # Ambil stock moves dari BU/PICK dan BU/OUT berdasarkan picking_id pick_moves = self.env['stock.move.line'].search([('picking_id', '=', picking_id)]) out_moves = self.env['stock.move.line'].search([('picking_id', '=', picking.linked_out_picking_id.id)]) - # Sesuaikan product_id di BU/OUT dengan BU/PICK for pick_move in pick_moves: corresponding_out_move = out_moves.filtered(lambda m: m.product_id == pick_move.product_id) if corresponding_out_move: corresponding_out_move.qty_done += pick_move.qty_done def _reset_qty_done_if_no_scan(self, picking_id): - """Set qty_done ke 0 hanya jika tidak ada scan.koli tersisa untuk picking_id tersebut.""" product_bu_pick = self.env['stock.move.line'].search([('picking_id', '=', picking_id)]) for move in product_bu_pick: @@ -1756,6 +1743,18 @@ class KonfirmKoli(models.Model): ) pick_id = fields.Many2one('stock.picking', string='Pick') + @api.constrains('pick_id') + def _check_duplicate_pick_id(self): + for rec in self: + exist = self.search([ + ('pick_id', '=', rec.pick_id.id), + ('picking_id', '=', rec.picking_id.id), + ('id', '!=', rec.id), + ]) + + if exist: + raise UserError(f"⚠️ '{rec.pick_id.display_name}' sudah discan untuk picking ini!") + class WarningModalWizard(models.TransientModel): _name = 'warning.modal.wizard' _description = 'Peringatan Koli Belum Diperiksa' @@ -1765,7 +1764,6 @@ class WarningModalWizard(models.TransientModel): picking_id = fields.Many2one('stock.picking') def action_continue(self): - """Lanjutkan validasi setelah menutup wizard""" if self.picking_id: return self.picking_id.with_context(skip_koli_check=True).button_validate() return {'type': 'ir.actions.act_window_close'} -- cgit v1.2.3 From e1edb44855ed2549f6f5a35773c5fdb81de9c0b4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 10 Apr 2025 09:07:24 +0700 Subject: comment function check_product_bom on action confirm so --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8d9af692..8d156943 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1257,7 +1257,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: - order.check_product_bom() + # order.check_product_bom() order.check_credit_limit() order.check_limit_so_to_invoice() if self.validate_different_vendor() and not self.vendor_approval: -- cgit v1.2.3 From 4cfda3f1511ba1e6f8226652cf1ff64a48efef92 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 10 Apr 2025 09:18:51 +0700 Subject: uncomment function check_product_bom on so --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8d156943..8d9af692 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1257,7 +1257,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: - # order.check_product_bom() + order.check_product_bom() order.check_credit_limit() order.check_limit_so_to_invoice() if self.validate_different_vendor() and not self.vendor_approval: -- cgit v1.2.3 From 00c69ce93bdb0071cd563be855857d2137115868 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 10 Apr 2025 13:36:30 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 35 ++++++++++++++++++++++++++++--- indoteknik_custom/views/stock_picking.xml | 3 ++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index be033b39..3aa18233 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -195,6 +195,12 @@ class StockPicking(models.Model): # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') + @api.constrains('scan_koli_lines') + def _constrains_scan_koli_lines(self): + now = datetime.datetime.utcnow() + for picking in self: + if len(picking.scan_koli_lines) > 0: + picking.driver_departure_date = now @api.depends('total_so_koli') def _compute_total_so_koli(self): @@ -1568,6 +1574,19 @@ class CheckKoli(models.Model): ) koli = fields.Char(string='Koli') reserved_id = fields.Many2one('stock.picking', string='Reserved Picking') + check_koli_progress = fields.Char( + string="Progress Check Koli" + ) + + @api.constrains('koli') + def _check_koli_progress(self): + for check in self: + if check.picking_id: + all_checks = self.env['check.koli'].search([('picking_id', '=', check.picking_id.id)], order='id') + if all_checks: + check_index = list(all_checks).index(check) + 1 # Nomor urut check + total_so_koli = len(all_checks) + check.check_koli_progress = f"{check_index}/{total_so_koli}" if total_so_koli else "0/0" class ScanKoli(models.Model): _name = 'scan.koli' @@ -1589,6 +1608,15 @@ class ScanKoli(models.Model): compute="_compute_scan_koli_progress" ) + def _compute_scan_koli_progress(self): + for scan in self: + if scan.picking_id: + all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') + if all_scans: + scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan + total_so_koli = scan.picking_id.total_so_koli + scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" + @api.onchange('koli_id') def _onchange_koli_compare_with_konfirm_koli(self): if not self.koli_id: @@ -1661,9 +1689,10 @@ class ScanKoli(models.Model): for scan in self: if scan.picking_id: all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') - scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan - total_so_koli = scan.picking_id.total_so_koli - scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" + if all_scans: + scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan + total_so_koli = scan.picking_id.total_so_koli + scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 67593b5b..d6850b2f 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -244,9 +244,10 @@ check.koli.tree check.koli - + + -- cgit v1.2.3 From b3ae9b237bb4ec2861ab6e1e6fc2fad85358fe77 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 10 Apr 2025 13:37:20 +0700 Subject: add brand ryu spareparts on purchasing job --- indoteknik_custom/models/purchasing_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py index 862e72c7..ea2f46cb 100644 --- a/indoteknik_custom/models/purchasing_job.py +++ b/indoteknik_custom/models/purchasing_job.py @@ -67,7 +67,7 @@ class PurchasingJob(models.Model): max(pjs.note::text) AS note, max(pjs.date_po::text) AS date_po, CASE - WHEN pmp.brand IN ('Tekiro', 'RYU', 'Rexco') THEN 27 + WHEN pmp.brand IN ('Tekiro', 'RYU', 'Rexco', 'RYU (Sparepart)') THEN 27 WHEN sub.vendor_id = 9688 THEN 397 WHEN sub.vendor_id = 35475 THEN 397 WHEN sub.vendor_id = 29712 THEN 397 -- cgit v1.2.3 From 10e3915bccc758ffc2f6e0e1d2e19f590605339e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 10 Apr 2025 14:37:06 +0700 Subject: add optional hide on commision tree --- indoteknik_custom/views/customer_commision.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index a78f8a9f..9f0e1e8a 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -17,8 +17,8 @@ decoration-danger="payment_status == 'pending'" widget="badge"/> - - + + -- cgit v1.2.3 From 4147989e776d82a0a8b06a0ff8901e2146b0bd57 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 11 Apr 2025 13:52:14 +0700 Subject: comment validasi validate bu out --- indoteknik_custom/models/stock_picking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b1243e95..18edd497 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -995,11 +995,11 @@ class StockPicking(models.Model): if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): self.state_approve_md = 'done' - if len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': - raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) + # if len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + # raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) - if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': - raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + # if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': + # raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From a24177e4f4f575ea95ebc1d886b830da5c320690 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 11 Apr 2025 14:01:52 +0700 Subject: fix case old so and new so wms validation --- indoteknik_custom/models/stock_picking.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 18edd497..1987c03c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -995,11 +995,19 @@ class StockPicking(models.Model): if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): self.state_approve_md = 'done' - # if len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': - # raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) - - # if len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': - # raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + threshold_datetime = datetime(2025, 4, 11, 13, 26) + + if (len(self.konfirm_koli_lines) == 0 + and 'BU/OUT/' in self.name + and self.picking_type_code == 'outgoing' + and (self.create_date or datetime.now()) > threshold_datetime): + raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) + + if (len(self.scan_koli_lines) == 0 + and 'BU/OUT/' in self.name + and self.picking_type_code == 'outgoing' + and (self.create_date or datetime.now()) > threshold_datetime): + raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From 52b493aaee7c1782c328d2f3af7bee6534342734 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 11 Apr 2025 14:06:29 +0700 Subject: fix error --- indoteknik_custom/models/stock_picking.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1987c03c..932e394b 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -995,18 +995,18 @@ class StockPicking(models.Model): if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): self.state_approve_md = 'done' - threshold_datetime = datetime(2025, 4, 11, 13, 26) + threshold_datetime = waktu(2025, 4, 11, 13, 26) if (len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing' - and (self.create_date or datetime.now()) > threshold_datetime): + and (self.create_date or waktu.utcnow()) > threshold_datetime): raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) if (len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing' - and (self.create_date or datetime.now()) > threshold_datetime): + and (self.create_date or waktu.utcnow()) > threshold_datetime): raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': -- cgit v1.2.3 From 9ca20d51a0aad50ea3df9bd878735c2fb8aadcc3 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 11 Apr 2025 15:32:39 +0700 Subject: push wms --- indoteknik_custom/models/stock_picking.py | 23 +++++++++++++++++------ indoteknik_custom/views/stock_picking.xml | 3 ++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 932e394b..19b7517c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -148,8 +148,16 @@ class StockPicking(models.Model): # for record in self: # record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih" quantity_koli = fields.Float(string="Quantity Koli", copy=False) + total_mapping_koli = fields.Float(string="Total Mapping Koli", compute='_compute_total_mapping_koli') - + @api.depends('konfirm_koli_lines', 'konfirm_koli_lines.pick_id', 'konfirm_koli_lines.pick_id.quantity_koli') + def _compute_total_mapping_koli(self): + for record in self: + total = 0.0 + for line in record.konfirm_koli_lines: + if line.pick_id and line.pick_id.quantity_koli: + total += line.pick_id.quantity_koli + record.total_mapping_koli = total @api.model def _compute_dokumen_tanda_terima(self): @@ -995,18 +1003,18 @@ class StockPicking(models.Model): if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): self.state_approve_md = 'done' - threshold_datetime = waktu(2025, 4, 11, 13, 26) + threshold_datetime = waktu(2025, 4, 11, 6, 26) if (len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing' - and (self.create_date or waktu.utcnow()) > threshold_datetime): + and self.create_date > threshold_datetime): raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) if (len(self.scan_koli_lines) == 0 and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing' - and (self.create_date or waktu.utcnow()) > threshold_datetime): + and self.create_date > threshold_datetime): raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': @@ -1015,9 +1023,12 @@ class StockPicking(models.Model): if len(self.check_koli_lines) == 0 and 'BU/PICK/' in self.name: raise UserError(_("Tidak ada koli! Harap periksa kembali.")) + if len(self.check_product_lines) == 0 and 'BU/PICK/' in self.name: + raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") - % (self.total_koli, self.total_so_koli)) + % (self.total_koli, self.t1otal_so_koli)) 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: @@ -1847,7 +1858,7 @@ class KonfirmKoli(models.Model): if exist: raise UserError(f"⚠️ '{rec.pick_id.display_name}' sudah discan untuk picking ini!") - + class WarningModalWizard(models.TransientModel): _name = 'warning.modal.wizard' _description = 'Peringatan Koli Belum Diperiksa' diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 7b4ba2f8..7d1153e0 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,7 +20,7 @@ - + @@ -89,6 +89,7 @@ + -- cgit v1.2.3 From 1def3707b2392fa17fb71cc70051bbe76cda47aa Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 11 Apr 2025 17:03:27 +0700 Subject: change request state reserve stock picking --- indoteknik_custom/models/stock_picking.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 19b7517c..fd9daec9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -649,13 +649,13 @@ class StockPicking(models.Model): def check_state_reserve(self): pickings = self.search([ ('state', 'not in', ['cancel', 'draft', 'done']), - ('picking_type_code', '=', 'outgoing'), - ('name', 'ilike', 'BU/OUT/'), + ('picking_type_code', '=', 'internal'), + ('name', 'ilike', 'BU/PICK/'), ]) count = self.search_count([ ('state', 'not in', ['cancel', 'draft', 'done']), - ('picking_type_code', '=', 'outgoing') + ('picking_type_code', '=', 'internal') ]) for picking in pickings: @@ -675,8 +675,8 @@ class StockPicking(models.Model): def check_state_reserve_backorder(self): pickings = self.search([ ('backorder_id', '!=', False), - ('name', 'ilike', 'BU/OUT/'), - ('picking_type_code', '=', 'outgoing'), + ('name', 'ilike', 'BU/PICK/'), + ('picking_type_code', '=', 'internal'), ('state', 'not in', ['cancel', 'draft', 'done']) ]) -- cgit v1.2.3 From 487772c771e72a77ae4d9e9c865d94d015f1ea5b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Apr 2025 10:44:12 +0700 Subject: refactor code state_reserve --- indoteknik_custom/models/stock_picking.py | 29 +---------------------------- indoteknik_custom/views/stock_picking.xml | 1 + 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index fd9daec9..558e13e6 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -215,6 +215,7 @@ class StockPicking(models.Model): # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') + shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id') @api.constrains('scan_koli_lines') def _constrains_scan_koli_lines(self): @@ -626,37 +627,15 @@ class StockPicking(models.Model): res = super(StockPicking, self).do_unreserve() current_time = datetime.datetime.utcnow() self.date_unreserve = current_time - # self.check_state_reserve() return res - # def check_state_reserve(self): - # do = self.search([ - # ('state', 'not in', ['cancel', 'draft', 'done']), - # ('picking_type_code', '=', 'outgoing') - # ]) - - # for rec in do: - # rec.state_reserve = 'ready' - # rec.date_reserved = datetime.datetime.utcnow() - - # for line in rec.move_ids_without_package: - # if line.product_uom_qty > line.reserved_availability: - # rec.state_reserve = 'waiting' - # rec.date_reserved = '' - # break - def check_state_reserve(self): pickings = self.search([ ('state', 'not in', ['cancel', 'draft', 'done']), ('picking_type_code', '=', 'internal'), ('name', 'ilike', 'BU/PICK/'), ]) - - count = self.search_count([ - ('state', 'not in', ['cancel', 'draft', 'done']), - ('picking_type_code', '=', 'internal') - ]) for picking in pickings: fullfillments = self.env['sales.order.fulfillment.v2'].search([ @@ -679,12 +658,6 @@ class StockPicking(models.Model): ('picking_type_code', '=', 'internal'), ('state', 'not in', ['cancel', 'draft', 'done']) ]) - - count = self.search_count([ - ('backorder_id', '!=', False), - ('picking_type_code', '=', 'outgoing'), - ('state', 'not in', ['cancel', 'draft', 'done']) - ]) for picking in pickings: fullfillments = self.env['sales.order.fulfillment.v2'].search([ diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 7d1153e0..5424f3d3 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -84,6 +84,7 @@ /> + -- cgit v1.2.3 From 17b8688b83b65b0c21034ffcc9e51baf1099618b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Apr 2025 11:19:41 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 558e13e6..2aca5003 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -978,17 +978,17 @@ class StockPicking(models.Model): threshold_datetime = waktu(2025, 4, 11, 6, 26) - if (len(self.konfirm_koli_lines) == 0 - and 'BU/OUT/' in self.name - and self.picking_type_code == 'outgoing' - and self.create_date > threshold_datetime): - raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) - - if (len(self.scan_koli_lines) == 0 - and 'BU/OUT/' in self.name - and self.picking_type_code == 'outgoing' - and self.create_date > threshold_datetime): - raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + # if (len(self.konfirm_koli_lines) == 0 + # and 'BU/OUT/' in self.name + # and self.picking_type_code == 'outgoing' + # and self.create_date > threshold_datetime): + # raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) + + # if (len(self.scan_koli_lines) == 0 + # and 'BU/OUT/' in self.name + # and self.picking_type_code == 'outgoing' + # and self.create_date > threshold_datetime): + # raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From 14fb9b00d18a7a0a3746106a1303f4ff1c13c356 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Apr 2025 11:21:06 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 2aca5003..558e13e6 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -978,17 +978,17 @@ class StockPicking(models.Model): threshold_datetime = waktu(2025, 4, 11, 6, 26) - # if (len(self.konfirm_koli_lines) == 0 - # and 'BU/OUT/' in self.name - # and self.picking_type_code == 'outgoing' - # and self.create_date > threshold_datetime): - # raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) - - # if (len(self.scan_koli_lines) == 0 - # and 'BU/OUT/' in self.name - # and self.picking_type_code == 'outgoing' - # and self.create_date > threshold_datetime): - # raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + if (len(self.konfirm_koli_lines) == 0 + and 'BU/OUT/' in self.name + and self.picking_type_code == 'outgoing' + and self.create_date > threshold_datetime): + raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali.")) + + if (len(self.scan_koli_lines) == 0 + and 'BU/OUT/' in self.name + and self.picking_type_code == 'outgoing' + and self.create_date > threshold_datetime): + raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From b73d16bf8dc0546190c9853f3e32a9aeaae3c1f0 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Apr 2025 13:17:02 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 558e13e6..4e926e60 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1056,6 +1056,7 @@ class StockPicking(models.Model): res = super(StockPicking, self).button_validate() self.calculate_line_no() self.date_done = datetime.datetime.utcnow() + self.driver_departure_date = datetime.datetime.utcnow() self.state_reserve = 'done' self.final_seq = 0 self.send_koli_to_so() -- cgit v1.2.3 From a4d19c6b9f026cc247c135b14a6fecf76a9fcd70 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Apr 2025 15:17:17 +0700 Subject: push --- indoteknik_api/controllers/api_v1/stock_picking.py | 6 +-- indoteknik_custom/models/stock_picking.py | 55 +++++++++++++++++++++- indoteknik_custom/views/stock_picking.xml | 2 + 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 55e07152..a2cd0557 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -116,10 +116,10 @@ class StockPicking(controller.Controller): return self.response(picking.get_tracking_detail()) - @http.route(prefix + 'stock-picking//documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + @http.route(prefix + 'stock-picking//documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_stock_picking_documentation(self, **kw): - picking_code = int(kw.get('picking_code', 0)) + id = int(kw.get('id', 0)) sj_document = kw.get('sj_document', False) paket_document = kw.get('paket_document', False) @@ -128,7 +128,7 @@ class StockPicking(controller.Controller): 'driver_arrival_date': datetime.utcnow(), } - picking_data = request.env['stock.picking'].search([('picking_code', '=', picking_code)], limit=1) + picking_data = request.env['stock.picking'].search([('id', '=', id)], limit=1) if not picking_data: return self.response(code=404, description='picking not found') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4e926e60..76ba51d4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -76,6 +76,11 @@ class StockPicking(models.Model): readonly=True, copy=False ) + out_code = fields.Integer( + string="Out Code", + readonly=True, + related="id", + ) sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan", ) paket_documentation = fields.Binary(string="Dokumentasi Paket", ) sj_return_date = fields.Datetime(string="SJ Return Date", ) @@ -216,6 +221,16 @@ class StockPicking(models.Model): # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id') + state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') + + @api.constrains('konfirm_koli_lines') + def _constrains_konfirm_koli_lines(self): + now = datetime.datetime.utcnow() + for picking in self: + if len(picking.konfirm_koli_lines) > 0: + picking.state_packing = 'packing_done' + else: + picking.state_packing = 'not_packing' @api.constrains('scan_koli_lines') def _constrains_scan_koli_lines(self): @@ -1059,6 +1074,7 @@ class StockPicking(models.Model): self.driver_departure_date = datetime.datetime.utcnow() self.state_reserve = 'done' self.final_seq = 0 + self.set_picking_code_out() self.send_koli_to_so() if not self.env.context.get('skip_koli_check'): for picking in self: @@ -1088,6 +1104,26 @@ class StockPicking(models.Model): self.send_mail_bills() return res + def set_picking_code_out(self): + for picking in self: + # Check if picking meets criteria + is_bu_pick = picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name + if not is_bu_pick: + continue + + # Find matching outgoing transfers + bu_out_transfers = self.search([ + ('name', 'like', 'BU/OUT/%'), + ('sale_id', '=', picking.sale_id.id), + ('picking_type_code', '=', 'outgoing'), + ('picking_code', '=', False), + ('state', 'not in', ['done', 'cancel']) + ]) + + # Assign sequence code to each matching transfer + for transfer in bu_out_transfers: + transfer.picking_code = self.env['ir.sequence'].next_by_code('stock.picking.code') + def check_koli(self): for picking in self: @@ -1206,11 +1242,28 @@ class StockPicking(models.Model): def create(self, vals): self._use_faktur(vals) records = super(StockPicking, self).create(vals) - + + # Panggil sync_sale_line setelah record dibuat + # records.sync_sale_line(vals) return records + def sync_sale_line(self, vals): + # Pastikan kita bekerja dengan record yang sudah ada + for picking in self: + if picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name: + for line in picking.move_ids_without_package: + if line.product_id and picking.sale_id: + sale_line = self.env['sale.order.line'].search([ + ('product_id', '=', line.product_id.id), + ('order_id', '=', picking.sale_id.id) + ], limit=1) # Tambahkan limit=1 untuk efisiensi + + if sale_line: + line.sale_line_id = sale_line.id + def write(self, vals): self._use_faktur(vals) + self.sync_sale_line(vals) for picking in self: # Periksa apakah kondisi terpenuhi saat data diubah if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 5424f3d3..f7b6134d 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -19,6 +19,7 @@ + - +
+ -- cgit v1.2.3 From e16dd5348125a7fc328052b177635282f931ccd9 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 6 May 2025 16:52:44 +0700 Subject: fix logic ask cancel purchasing --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0d86019e..f99058ea 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1497,7 +1497,7 @@ class SaleOrder(models.Model): def action_cancel(self): # TODO stephan prevent cancel if have invoice, do, and po - if self.state_ask_cancel != 'approve': + if self.state_ask_cancel != 'approve' and self.state not in ['draft', 'sent']: raise UserError("Anda harus approval purchasing terlebih dahulu") main_parent = self.partner_id.get_main_parent() if self._name != 'sale.order': -- cgit v1.2.3 From 1cf66814711b428dd9782292d3403bb9c78b36a2 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 6 May 2025 17:06:09 +0700 Subject: fix when checkout different product category and add tnc --- indoteknik_custom/models/voucher.py | 207 ++++++++++++++++++-------- indoteknik_custom/models/website_user_cart.py | 121 ++++++++------- indoteknik_custom/views/voucher.xml | 74 +++++---- 3 files changed, 254 insertions(+), 148 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 7b458d01..baed8062 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -11,43 +11,47 @@ class Voucher(models.Model): name = fields.Char(string='Name') image = fields.Binary(string='Image') code = fields.Char(string='Code', help='Kode voucher yang akan berlaku untuk pengguna') + voucher_category = fields.Many2many('product.public.category', string='Category Voucher', + help='Kategori Produk yang dapat menggunakan voucher ini') description = fields.Text(string='Description') discount_amount = fields.Float(string='Discount Amount') - discount_type = fields.Selection(string='Discount Type', - selection=[ - ('percentage', 'Percentage'), - ('fixed_price', 'Fixed Price'), - ], - help='Select the type of discount:\n' - '- Percentage: Persentase dari total harga.\n' - '- Fixed Price: Jumlah tetap yang dikurangi dari harga total.' - ) - visibility = fields.Selection(string='Visibility', - selection=[ - ('public', 'Public'), - ('private', 'Private') - ], - help='Select the visibility:\n' - '- Public: Ditampilkan kepada seluruh pengguna.\n' - '- Private: Tidak ditampilkan kepada seluruh pengguna.' - ) + discount_type = fields.Selection(string='Discount Type', + selection=[ + ('percentage', 'Percentage'), + ('fixed_price', 'Fixed Price'), + ], + help='Select the type of discount:\n' + '- Percentage: Persentase dari total harga.\n' + '- Fixed Price: Jumlah tetap yang dikurangi dari harga total.' + ) + visibility = fields.Selection(string='Visibility', + selection=[ + ('public', 'Public'), + ('private', 'Private') + ], + help='Select the visibility:\n' + '- Public: Ditampilkan kepada seluruh pengguna.\n' + '- Private: Tidak ditampilkan kepada seluruh pengguna.' + ) start_time = fields.Datetime(string='Start Time') end_time = fields.Datetime(string='End Time') - min_purchase_amount = fields.Integer(string='Min. Purchase Amount', help='Nominal minimum untuk dapat menggunakan voucher. Isi 0 jika tidak ada minimum purchase amount') + min_purchase_amount = fields.Integer(string='Min. Purchase Amount', + help='Nominal minimum untuk dapat menggunakan voucher. Isi 0 jika tidak ada minimum purchase amount') max_discount_amount = fields.Integer(string='Max. Discount Amount', help='Max nominal terhadap persentase diskon') order_ids = fields.One2many('sale.order', 'applied_voucher_id', string='Order') limit = fields.Integer( - string='Limit', + string='Limit', default=0, help='Batas penggunaan voucher keseluruhan. Isi dengan angka 0 untuk penggunaan tanpa batas' ) limit_user = fields.Integer( - string='Limit User', + string='Limit User', default=0, help='Batas penggunaan voucher per pengguna. Misalnya, jika diisi dengan angka 1, maka setiap pengguna hanya dapat menggunakan voucher ini satu kali. Isi dengan angka 0 untuk penggunaan tanpa batas' ) manufacture_ids = fields.Many2many('x_manufactures', string='Brands', help='Voucher appplied only for brand') - excl_pricelist_ids = fields.Many2many('product.pricelist', string='Excluded Pricelists', help='Hide voucher from selected exclude pricelist') + excl_pricelist_ids = fields.Many2many('product.pricelist', string='Excluded Pricelists', + help='Hide voucher from selected exclude pricelist') voucher_line = fields.One2many('voucher.line', 'voucher_id', 'Voucher Line') terms_conditions = fields.Html('Terms and Conditions') apply_type = fields.Selection(string='Apply Type', default="all", selection=[ @@ -64,12 +68,51 @@ class Voucher(models.Model): ('person', "Account Individu"), ('company', "Account Company"), ]) + + def is_voucher_applicable(self, product_id): + if not self.voucher_category: + return True + + public_categories = product_id.public_categ_ids + + return bool(set(public_categories.ids) & set(self.voucher_category.ids)) + + def is_voucher_applicable_for_category(self, category): + import logging + _logger = logging.getLogger(__name__) + + # If voucher has no category restrictions, it applies to all + if not self.voucher_category: + _logger.info("Voucher %s has no category restrictions", self.code) + return True + + # Check if the product's category directly matches one of the voucher's categories + if category.id in self.voucher_category.ids: + _logger.info("Category %s directly matches voucher %s", category.name, self.code) + return True + + # Build the category hierarchy path for the product's category + category_path = [] + current_cat = category + while current_cat: + category_path.append(current_cat.id) + current_cat = current_cat.parent_id + + # Check if any of the voucher's categories are in the category path (parent categories) + for voucher_cat in self.voucher_category: + if voucher_cat.id in category_path: + _logger.info("Voucher category %s is in the category path of %s", voucher_cat.name, category.name) + return True + + _logger.info("No applicable category found for voucher %s and category %s", self.code, category.name) + return False + @api.constrains('description') def _check_description_length(self): for record in self: if record.description and len(record.description) > 120: raise ValidationError('Deskripsi tidak boleh lebih dari 120 karakter') - + @api.constrains('limit', 'limit_user') def _check_limit(self): for rec in self: @@ -87,7 +130,7 @@ class Voucher(models.Model): def res_format(self): datas = [voucher.format() for voucher in self] return datas - + def format(self): ir_attachment = self.env['ir.attachment'] data = { @@ -100,7 +143,7 @@ class Voucher(models.Model): 'remaining_time': self._res_remaining_time(), } return data - + def _res_remaining_time(self): seconds = self._get_remaining_time() remaining_time = timedelta(seconds=seconds) @@ -116,14 +159,31 @@ class Voucher(models.Model): time = minutes unit = 'menit' return f'{time} {unit}' - + def _get_remaining_time(self): calculate_time = self.end_time - datetime.now() return round(calculate_time.total_seconds()) - + def filter_order_line(self, order_line): + import logging + _logger = logging.getLogger(__name__) + voucher_manufacture_ids = self.collect_manufacture_ids() results = [] + + if self.voucher_category and len(order_line) > 0: + for line in order_line: + category_applicable = False + for category in line['product_id'].public_categ_ids: + if self.is_voucher_applicable_for_category(category): + category_applicable = True + break + + if not category_applicable: + _logger.info("Cart contains product %s with non-applicable category - voucher %s cannot be used", + line['product_id'].name, self.code) + return [] + for line in order_line: manufacture_id = line['product_id'].x_manufacture.id or None if self.apply_type == 'brand' and manufacture_id not in voucher_manufacture_ids: @@ -132,35 +192,36 @@ class Voucher(models.Model): product_flashsale = line['product_id']._get_active_flash_sale() if len(product_flashsale) > 0: continue - + results.append(line) - + return results - + def calc_total_order_line(self, order_line): - result = { 'all': 0, 'brand': {} } + result = {'all': 0, 'brand': {}} for line in order_line: manufacture_id = line['product_id'].x_manufacture.id or None manufacture_total = result['brand'].get(manufacture_id, 0) result['brand'][manufacture_id] = manufacture_total + line['subtotal'] result['all'] += line['subtotal'] - + return result - + def calc_discount_amount(self, total): - result = { 'all': 0, 'brand': {} } + result = {'all': 0, 'brand': {}} if self.apply_type in ['all', 'shipping']: if total['all'] < self.min_purchase_amount: return result - + if self.discount_type == 'percentage': decimal_discount = self.discount_amount / 100 discount_all = total['all'] * decimal_discount - result['all'] = min(discount_all, self.max_discount_amount) if self.max_discount_amount > 0 else discount_all + result['all'] = min(discount_all, + self.max_discount_amount) if self.max_discount_amount > 0 else discount_all else: result['all'] = min(self.discount_amount, total['all']) - + return result for line in self.voucher_line: @@ -173,99 +234,115 @@ class Voucher(models.Model): elif line.discount_type == 'percentage': decimal_discount = line.discount_amount / 100 discount_brand = total_brand * decimal_discount - discount_brand = min(discount_brand, line.max_discount_amount) if line.max_discount_amount > 0 else discount_brand + discount_brand = min(discount_brand, + line.max_discount_amount) if line.max_discount_amount > 0 else discount_brand else: discount_brand = min(line.discount_amount, total_brand) - + result['brand'][manufacture_id] = round(discount_brand, 2) result['all'] += discount_brand - + result['all'] = round(result['all'], 2) return result def apply(self, order_line): - order_line = self.filter_order_line(order_line) - amount_total = self.calc_total_order_line(order_line) + + filtered_order_line = self.filter_order_line(order_line) + + amount_total = self.calc_total_order_line(filtered_order_line) + discount = self.calc_discount_amount(amount_total) + return { 'discount': discount, 'total': amount_total, 'type': self.apply_type, - 'valid_order': order_line + 'valid_order': filtered_order_line, } - + def collect_manufacture_ids(self): return [x.manufacture_id.id for x in self.voucher_line] - + def calculate_discount(self, price): if price < self.min_purchase_amount: return 0 - + if self.discount_type == 'fixed_price': return self.discount_amount - + if self.discount_type == 'percentage': discount = price * self.discount_amount / 100 max_disc = self.max_discount_amount return discount if max_disc == 0 else min(discount, max_disc) - + return 0 - + def get_active_voucher(self, domain): current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') domain += [ ('start_time', '<=', current_time), ('end_time', '>=', current_time), ] - vouchers = self.search(domain, order='min_purchase_amount ASC') + vouchers = self.search(domain, order='min_purchase_amount ASC') return vouchers - + def generate_tnc(self): tnc = [] tnc.append('
    ') - tnc.append('
  1. Voucher hanya berlaku apabila pembelian Pengguna sudah memenuhi syarat dan ketentuan yang tertera pada voucher
  2. ') + tnc.append( + '
  3. Voucher hanya berlaku apabila pembelian Pengguna sudah memenuhi syarat dan ketentuan yang tertera pada voucher
  4. ') tnc.append(f'
  5. Voucher berlaku {self._res_remaining_time()} lagi
  6. ') tnc.append(f'
  7. Voucher tidak bisa digunakan apabila terdapat produk flash sale
  8. ') + if self.voucher_category: + category_names = ', '.join([cat.name for cat in self.voucher_category]) + tnc.append( + f'
  9. Voucher hanya berlaku untuk produk dalam kategori {category_names} dan sub-kategorinya
  10. ') + tnc.append( + f'
  11. Voucher tidak dapat digunakan jika ada produk di keranjang yang tidak termasuk dalam kategori tersebut
  12. ') + if len(self.voucher_line) > 0: - brand_names = ', '.join([x.manufacture_id.x_name or '' for x in self.voucher_line]) - tnc.append(f'
  13. Voucher berlaku untuk produk dari brand {brand_names}
  14. ') + tnc.append(f'
  15. Voucher berlaku untuk produk dari brand terpilih
  16. ') tnc.append(self.generate_detail_tnc()) tnc.append('
') - + return ' '.join(tnc) - + def generate_detail_tnc(self): def format_currency(amount): formatted_number = '{:,.0f}'.format(amount).replace(',', '.') return f'Rp{formatted_number}' - + tnc = [] if self.apply_type == 'all': tnc.append('
  • ') tnc.append('Nominal potongan yang bisa didapatkan sebesar') - tnc.append(f'{self.discount_amount}%' if self.discount_type == 'percentage' else format_currency(self.discount_amount)) - + tnc.append(f'{self.discount_amount}%' if self.discount_type == 'percentage' else format_currency( + self.discount_amount)) + if self.discount_type == 'percentage' and self.max_discount_amount > 0: tnc.append(f'hingga {format_currency(self.max_discount_amount)}') - tnc.append(f'dengan minimum pembelian {format_currency(self.min_purchase_amount)}' if self.min_purchase_amount > 0 else 'tanpa minimum pembelian') + tnc.append( + f'dengan minimum pembelian {format_currency(self.min_purchase_amount)}' if self.min_purchase_amount > 0 else 'tanpa minimum pembelian') tnc.append('
  • ') else: for line in self.voucher_line: line_tnc = [] line_tnc.append(f'Nominal potongan produk {line.manufacture_id.x_name} yang bisa didapatkan sebesar') - line_tnc.append(f'{line.discount_amount}%' if line.discount_type == 'percentage' else format_currency(line.discount_amount)) + line_tnc.append(f'{line.discount_amount}%' if line.discount_type == 'percentage' else format_currency( + line.discount_amount)) if line.discount_type == 'percentage' and line.max_discount_amount > 0: line_tnc.append(f'hingga {format_currency(line.max_discount_amount)}') - - line_tnc.append(f'dengan minimum pembelian {format_currency(line.min_purchase_amount)}' if line.min_purchase_amount > 0 else 'tanpa minimum pembelian') + + line_tnc.append( + f'dengan minimum pembelian {format_currency(line.min_purchase_amount)}' if line.min_purchase_amount > 0 else 'tanpa minimum pembelian') line_tnc = ' '.join(line_tnc) tnc.append(f'
  • {line_tnc}
  • ') return ' '.join(tnc) - # copy semua data kalau diduplicate + # copy semua data kalau diduplicate def copy(self, default=None): default = dict(default or {}) voucher_lines = [] @@ -280,4 +357,4 @@ class Voucher(models.Model): })) default['voucher_line'] = voucher_lines - return super(Voucher, self).copy(default) \ No newline at end of file + return super(Voucher, self).copy(default) diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py index 44393cf1..a6d08949 100644 --- a/indoteknik_custom/models/website_user_cart.py +++ b/indoteknik_custom/models/website_user_cart.py @@ -1,10 +1,11 @@ from odoo import fields, models, api from datetime import datetime, timedelta + class WebsiteUserCart(models.Model): _name = 'website.user.cart' _rec_name = 'user_id' - + user_id = fields.Many2one('res.users', string='User') product_id = fields.Many2one('product.product', string='Product') program_line_id = fields.Many2one('promotion.program.line', string='Program', help="Apply program") @@ -18,7 +19,8 @@ class WebsiteUserCart(models.Model): is_reminder = fields.Boolean(string='Reminder?') phone_user = fields.Char(string='Phone', related='user_id.mobile') price = fields.Float(string='Price', compute='_compute_price') - program_product_id = fields.Many2one('product.product', string='Program Products', compute='_compute_program_product_ids') + program_product_id = fields.Many2one('product.product', string='Program Products', + compute='_compute_program_product_ids') @api.depends('program_line_id') def _compute_program_product_ids(self): @@ -55,6 +57,12 @@ class WebsiteUserCart(models.Model): product = self.product_id.v2_api_single_response(self.product_id) res.update(product) + # Add category information + res['categories'] = [{ + 'id': cat.id, + 'name': cat.name + } for cat in self.product_id.public_categ_ids] + # Check if the product's inventory location is in ID 57 or 83 target_locations = [57, 83] stock_quant = self.env['stock.quant'].search([ @@ -90,7 +98,14 @@ class WebsiteUserCart(models.Model): def get_products(self): products = [x.get_product() for x in self] - + + for i, cart_item in enumerate(self): + if cart_item.product_id and i < len(products): + products[i]['categories'] = [{ + 'id': cat.id, + 'name': cat.name + } for cat in cart_item.product_id.public_categ_ids] + return products def get_product_by_user(self, user_id, selected=False, source=False): @@ -121,10 +136,10 @@ class WebsiteUserCart(models.Model): products = products_active.get_products() return products - + def get_user_checkout(self, user_id, voucher=False, voucher_shipping=False, source=False): products = self.get_product_by_user(user_id=user_id, selected=True, source=source) - + total_purchase = 0 total_discount = 0 for product in products: @@ -132,9 +147,9 @@ class WebsiteUserCart(models.Model): price = product['package_price'] * product['quantity'] else: price = product['price']['price'] * product['quantity'] - + discount_price = price - product['price']['price_discount'] * product['quantity'] - + total_purchase += price total_discount += discount_price @@ -142,7 +157,7 @@ class WebsiteUserCart(models.Model): discount_voucher = 0 discount_voucher_shipping = 0 order_line = [] - + if voucher or voucher_shipping: for product in products: if product['cart_type'] == 'promotion': continue @@ -153,16 +168,16 @@ class WebsiteUserCart(models.Model): 'qty': product['quantity'], 'subtotal': product['subtotal'] }) - + if voucher: voucher_info = voucher.apply(order_line) discount_voucher = voucher_info['discount']['all'] subtotal -= discount_voucher - + if voucher_shipping: voucher_shipping_info = voucher_shipping.apply(order_line) - discount_voucher_shipping = voucher_shipping_info['discount']['all'] - + discount_voucher_shipping = voucher_shipping_info['discount']['all'] + tax = round(subtotal * 0.11) grand_total = subtotal + tax total_weight = sum(x['weight'] * x['quantity'] for x in products) @@ -179,28 +194,31 @@ class WebsiteUserCart(models.Model): 'kg': total_weight, 'g': total_weight * 1000 }, - 'has_product_without_weight': any(not product.get('weight') or product.get('weight') == 0 for product in products), + 'has_product_without_weight': any( + not product.get('weight') or product.get('weight') == 0 for product in products), 'products': products } return result - + def action_mail_reminder_to_checkout(self, limit=200): user_ids = self.search([('is_reminder', '=', False)]).mapped('user_id')[:limit] - + for user in user_ids: latest_cart = self.search([('user_id', '=', user.id)], order='create_date desc', limit=1) - + carts_to_remind = self.search([('user_id', '=', user.id)]) - + if latest_cart and not latest_cart.is_reminder: for cart in carts_to_remind: check = cart.check_product_flashsale(cart.product_id.id) - if not cart.program_line_id and cart.product_id.default_code and not 'BOM' in cart.product_id.default_code and check['is_flashsale'] == False: + if not cart.program_line_id and cart.product_id.default_code and not 'BOM' in cart.product_id.default_code and \ + check['is_flashsale'] == False: cart.is_selected = True - if cart.program_line_id or check['is_flashsale'] or cart.product_id.default_code and 'BOM' in cart.product_id.default_code: + if cart.program_line_id or check[ + 'is_flashsale'] or cart.product_id.default_code and 'BOM' in cart.product_id.default_code: cart.is_selected = False cart.is_reminder = True - + template = self.env.ref('indoteknik_custom.mail_template_user_cart_reminder_to_checkout') template.send_mail(latest_cart.id, force_send=True) @@ -234,8 +252,9 @@ class WebsiteUserCart(models.Model): break product_discount = subtotal_promo if cart.program_line_id or check['is_flashsale'] else subtotal - total_discount += product_discount - if check['is_flashsale'] == False and cart.product_id.default_code and not 'BOM' in cart.product_id.default_code: + total_discount += product_discount + if check[ + 'is_flashsale'] == False and cart.product_id.default_code and not 'BOM' in cart.product_id.default_code: voucher_product = subtotal * (discount_amount / 100.0) total_voucher += voucher_product @@ -253,14 +272,15 @@ class WebsiteUserCart(models.Model): def check_product_flashsale(self, product_id): product = product_id current_time = datetime.utcnow() - found_product = self.env['product.pricelist.item'].search([('product_id', '=', product_id), ('pricelist_id.is_flash_sale', '=', True)]) + found_product = self.env['product.pricelist.item'].search( + [('product_id', '=', product_id), ('pricelist_id.is_flash_sale', '=', True)]) if found_product: for found in found_product: pricelist_flashsale = found.pricelist_id if pricelist_flashsale.start_date <= current_time <= pricelist_flashsale.end_date: - return { + return { 'is_flashsale': True, 'price': found.fixed_price } @@ -269,10 +289,9 @@ class WebsiteUserCart(models.Model): 'is_flashsale': False } - return { + return { 'is_flashsale': False } - # if found_product: # start_date = found_product.pricelist_id.start_date @@ -291,26 +310,26 @@ class WebsiteUserCart(models.Model): # return { # 'is_flashsale': False # } - + def get_data_promo(self, program_line_id): program_line_product = self.env['promotion.product'].search([ ('program_line_id', '=', program_line_id) - ]) - + ]) + program_free_product = self.env['promotion.free_product'].search([ ('program_line_id', '=', program_line_id) - ]) + ]) return program_line_product, program_free_product - + def get_weight_product(self, program_line_id): program_line_product = self.env['promotion.product'].search([ ('program_line_id', '=', program_line_id) - ]) - + ]) + program_free_product = self.env['promotion.free_product'].search([ ('program_line_id', '=', program_line_id) - ]) - + ]) + real_weight = 0.0 if program_line_product: for product in program_line_product: @@ -321,16 +340,16 @@ class WebsiteUserCart(models.Model): real_weight += product.product_id.weight return real_weight - + def get_price_coret(self, program_line_id): program_line_product = self.env['promotion.product'].search([ ('program_line_id', '=', program_line_id) - ]) - + ]) + program_free_product = self.env['promotion.free_product'].search([ ('program_line_id', '=', program_line_id) - ]) - + ]) + price_coret = 0.0 for product in program_line_product: price = self.get_price_website(product.product_id.id) @@ -340,20 +359,22 @@ class WebsiteUserCart(models.Model): price = self.get_price_website(product.product_id.id) price_coret += price['price'] * product.qty - return price_coret - + return price_coret + def get_price_website(self, product_id): - price_website = self.env['product.pricelist.item'].search([('product_id', '=', product_id), ('pricelist_id', '=', 17022)], limit=1) - - price_tier = self.env['product.pricelist.item'].search([('product_id', '=', product_id), ('pricelist_id', '=', 17023)], limit=1) - + price_website = self.env['product.pricelist.item'].search( + [('product_id', '=', product_id), ('pricelist_id', '=', 17022)], limit=1) + + price_tier = self.env['product.pricelist.item'].search( + [('product_id', '=', product_id), ('pricelist_id', '=', 17023)], limit=1) + fixed_price = price_website.fixed_price if price_website else 0.0 discount = price_tier.price_discount if price_tier else 0.0 - + discounted_price = fixed_price - (fixed_price * discount / 100) - + final_price = discounted_price / 1.11 - + return { 'price': final_price, 'web_price': discounted_price @@ -365,4 +386,4 @@ class WebsiteUserCart(models.Model): def format_currency(self, number): number = int(number) - return "{:,}".format(number).replace(',', '.') \ No newline at end of file + return "{:,}".format(number).replace(',', '.') diff --git a/indoteknik_custom/views/voucher.xml b/indoteknik_custom/views/voucher.xml index ae958f05..7f258c1c 100755 --- a/indoteknik_custom/views/voucher.xml +++ b/indoteknik_custom/views/voucher.xml @@ -27,63 +27,71 @@ - - - + + + + - - - - + + + + - - - + + + - - + - - - - - + + + + + - - + - + @@ -92,10 +100,10 @@ \ No newline at end of file -- cgit v1.2.3 From 0c9f3297bfda30ec813833a5db3bcbf7a24f327d Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 7 May 2025 06:49:50 +0700 Subject: simplify tnc --- indoteknik_custom/models/voucher.py | 67 +++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index baed8062..0b47e5f0 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -309,39 +309,54 @@ class Voucher(models.Model): return ' '.join(tnc) def generate_detail_tnc(self): - def format_currency(amount): - formatted_number = '{:,.0f}'.format(amount).replace(',', '.') - return f'Rp{formatted_number}' + # def format_currency(amount): + # formatted_number = '{:,.0f}'.format(amount).replace(',', '.') + # return f'Rp{formatted_number}' tnc = [] if self.apply_type == 'all': tnc.append('
  • ') - tnc.append('Nominal potongan yang bisa didapatkan sebesar') - tnc.append(f'{self.discount_amount}%' if self.discount_type == 'percentage' else format_currency( - self.discount_amount)) - - if self.discount_type == 'percentage' and self.max_discount_amount > 0: - tnc.append(f'hingga {format_currency(self.max_discount_amount)}') - - tnc.append( - f'dengan minimum pembelian {format_currency(self.min_purchase_amount)}' if self.min_purchase_amount > 0 else 'tanpa minimum pembelian') + tnc.append('Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.') tnc.append('
  • ') - else: - for line in self.voucher_line: - line_tnc = [] - line_tnc.append(f'Nominal potongan produk {line.manufacture_id.x_name} yang bisa didapatkan sebesar') - line_tnc.append(f'{line.discount_amount}%' if line.discount_type == 'percentage' else format_currency( - line.discount_amount)) - - if line.discount_type == 'percentage' and line.max_discount_amount > 0: - line_tnc.append(f'hingga {format_currency(line.max_discount_amount)}') - - line_tnc.append( - f'dengan minimum pembelian {format_currency(line.min_purchase_amount)}' if line.min_purchase_amount > 0 else 'tanpa minimum pembelian') - line_tnc = ' '.join(line_tnc) - tnc.append(f'
  • {line_tnc}
  • ') + elif len(self.voucher_line) > 0: + tnc.append( + '
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') return ' '.join(tnc) + # def generate_detail_tnc(self): + # def format_currency(amount): + # formatted_number = '{:,.0f}'.format(amount).replace(',', '.') + # return f'Rp{formatted_number}' + # + # tnc = [] + # if self.apply_type == 'all': + # tnc.append('
  • ') + # tnc.append('Nominal potongan yang bisa didapatkan sebesar') + # tnc.append(f'{self.discount_amount}%' if self.discount_type == 'percentage' else format_currency( + # self.discount_amount)) + # + # if self.discount_type == 'percentage' and self.max_discount_amount > 0: + # tnc.append(f'hingga {format_currency(self.max_discount_amount)}') + # + # tnc.append( + # f'dengan minimum pembelian {format_currency(self.min_purchase_amount)}' if self.min_purchase_amount > 0 else 'tanpa minimum pembelian') + # tnc.append('
  • ') + # else: + # for line in self.voucher_line: + # line_tnc = [] + # line_tnc.append(f'Nominal potongan produk {line.manufacture_id.x_name} yang bisa didapatkan sebesar') + # line_tnc.append(f'{line.discount_amount}%' if line.discount_type == 'percentage' else format_currency( + # line.discount_amount)) + # + # if line.discount_type == 'percentage' and line.max_discount_amount > 0: + # line_tnc.append(f'hingga {format_currency(line.max_discount_amount)}') + # + # line_tnc.append( + # f'dengan minimum pembelian {format_currency(line.min_purchase_amount)}' if line.min_purchase_amount > 0 else 'tanpa minimum pembelian') + # line_tnc = ' '.join(line_tnc) + # tnc.append(f'
  • {line_tnc}
  • ') + # return ' '.join(tnc) + # copy semua data kalau diduplicate def copy(self, default=None): default = dict(default or {}) -- cgit v1.2.3 From 1775e7907c905e85f40cecfef69c651c45e64de3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 7 May 2025 09:18:32 +0700 Subject: remove logging --- indoteknik_custom/models/voucher.py | 8 ++++---- indoteknik_custom/views/voucher.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 0b47e5f0..cda9309f 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -165,8 +165,8 @@ class Voucher(models.Model): return round(calculate_time.total_seconds()) def filter_order_line(self, order_line): - import logging - _logger = logging.getLogger(__name__) + # import logging + # _logger = logging.getLogger(__name__) voucher_manufacture_ids = self.collect_manufacture_ids() results = [] @@ -180,8 +180,8 @@ class Voucher(models.Model): break if not category_applicable: - _logger.info("Cart contains product %s with non-applicable category - voucher %s cannot be used", - line['product_id'].name, self.code) + # _logger.info("Cart contains product %s with non-applicable category - voucher %s cannot be used", + # line['product_id'].name, self.code) return [] for line in order_line: diff --git a/indoteknik_custom/views/voucher.xml b/indoteknik_custom/views/voucher.xml index 7f258c1c..78e42969 100755 --- a/indoteknik_custom/views/voucher.xml +++ b/indoteknik_custom/views/voucher.xml @@ -29,7 +29,7 @@ - + -- cgit v1.2.3 From d3df18ba29101fbce6a249915436d741a70ad810 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 7 May 2025 09:57:44 +0700 Subject: (andri) tidak bisa edit data product jika sudah status PO (meskipun sudah di unlock) --- indoteknik_custom/views/purchase_order.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index d6ad2408..920268bc 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -140,7 +140,7 @@ - {'readonly': ['|', ('state', 'in', ['done', 'cancel']), ('has_active_invoice', '=', True)]} + {'readonly': ['|', ('state', 'in', ['purchase', 'done', 'cancel']), ('has_active_invoice', '=', True)]} -- cgit v1.2.3 From ab1c5cabe7bdcfb950a2d7201ce367eea49ad7f2 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 7 May 2025 09:59:45 +0700 Subject: fix date time utc --- indoteknik_custom/models/commision.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 32e81b9a..bd4c06a4 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -1,8 +1,10 @@ from odoo import models, api, fields, _ from odoo.exceptions import UserError from datetime import datetime +# import datetime import logging from terbilang import Terbilang +import pytz _logger = logging.getLogger(__name__) @@ -301,30 +303,34 @@ class CustomerCommision(models.Model): return result def action_confirm_customer_commision(self): - now = datetime.utcnow() + jakarta_tz = pytz.timezone('Asia/Jakarta') + now = datetime.now(jakarta_tz) + + now_naive = now.replace(tzinfo=None) + if not self.status or self.status == 'draft': self.status = 'pengajuan1' elif self.status == 'pengajuan1' and self.env.user.is_sales_manager: self.status = 'pengajuan2' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name - self.date_approved_sales = now + self.date_approved_sales = now_naive self.position_sales = 'Sales Manager' elif self.status == 'pengajuan2' and self.env.user.id == 19: self.status = 'pengajuan3' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name - self.date_approved_marketing = now + self.date_approved_marketing = now_naive self.position_marketing = 'Marketing Manager' elif self.status == 'pengajuan3' and self.env.user.is_leader: self.status = 'pengajuan4' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name - self.date_approved_pimpinan = now + self.date_approved_pimpinan = now_naive self.position_pimpinan = 'Pimpinan' elif self.status == 'pengajuan4' and self.env.user.id == 1272: for line in self.commision_lines: line.invoice_id.is_customer_commision = True self.status = 'approved' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name - self.date_approved_accounting = now + self.date_approved_accounting = now_naive self.position_accounting = 'Accounting' else: raise UserError('Harus di approved oleh yang bersangkutan') -- cgit v1.2.3 From a9a6d2b0bfdce88ebc42ac92fcbc016e30e8ff72 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Wed, 7 May 2025 13:39:57 +0700 Subject: bug fix error matches so in purchase order --- indoteknik_custom/models/automatic_purchase.py | 52 +++++++++++++++++++++++++- indoteknik_custom/security/ir.model.access.csv | 1 + 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index fbdf8dae..a39abba9 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -1,4 +1,4 @@ -from odoo import models, api, fields +from odoo import models, api, fields, tools from odoo.exceptions import UserError from datetime import datetime import logging, math @@ -284,7 +284,7 @@ class AutomaticPurchase(models.Model): def create_purchase_order_sales_match(self, purchase_order): matches_so_product_ids = [line.product_id.id for line in purchase_order.order_line] - matches_so = self.env['automatic.purchase.sales.match'].search([ + matches_so = self.env['v.sale.notin.matchpo'].search([ ('automatic_purchase_id', '=', self.id), ('sale_line_id.product_id', 'in', matches_so_product_ids), ]) @@ -292,6 +292,8 @@ class AutomaticPurchase(models.Model): sale_ids_set = set() sale_ids_name = set() for sale_order in matches_so: + # @stephan skip so line yang sudah pernah ada di purchase order sales match sebelumnya + salesperson_name = sale_order.sale_id.user_id.name sale_id_with_salesperson = f"{sale_order.sale_id.name} - {salesperson_name}" @@ -655,3 +657,49 @@ class SyncPurchasingJob(models.Model): outgoing = fields.Float(string="Outgoing") action = fields.Char(string="Status") date = fields.Datetime(string="Date Sync") + + +class SaleNotInMatchPO(models.Model): + # created by @stephan for speed up performance while create po from automatic purchase + _name = 'v.sale.notin.matchpo' + _auto = False + _rec_name = 'id' + + id = fields.Integer() + automatic_purchase_id = fields.Many2one('automatic.purchase', string='APO') + automatic_purchase_line_id = fields.Many2one('automatic.purchase.line', string='APO Line') + sale_id = fields.Many2one('sale.order', string='Sale') + sale_line_id = fields.Many2one('sale.order.line', string='Sale Line') + picking_id = fields.Many2one('stock.picking', string='Picking') + move_id = fields.Many2one('stock.move', string='Move') + partner_id = fields.Many2one('res.partner', string='Partner') + partner_invoice_id = fields.Many2one('res.partner', string='Partner Invoice') + salesperson_id = fields.Many2one('res.user', string='Salesperson') + product_id = fields.Many2one('product.product', string='Product') + qty_so = fields.Float(string='Qty SO') + qty_po = fields.Float(string='Qty PO') + create_uid = fields.Many2one('res.user', string='Created By') + create_date = fields.Datetime(string='Create Date') + write_uid = fields.Many2one('res.user', string='Updated By') + write_date = fields.Many2one(string='Updated') + purchase_price = fields.Many2one(string='Purchase Price') + purchase_tax_id = fields.Many2one('account.tax', string='Purchase Tax') + note_procurement = fields.Many2one(string='Note Procurement') + + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + self.env.cr.execute(""" + CREATE OR REPLACE VIEW %s AS( + select apsm.id, apsm.automatic_purchase_id, apsm.automatic_purchase_line_id, apsm.sale_id, apsm.sale_line_id, + apsm.picking_id, apsm.move_id, apsm.partner_id, + apsm.partner_invoice_id, apsm.salesperson_id, apsm.product_id, apsm.qty_so, apsm.qty_po, apsm.create_uid, + apsm.create_date, apsm.write_uid, apsm.write_date, apsm.purchase_price, + apsm.purchase_tax_id, apsm.note_procurement + from automatic_purchase_sales_match apsm + where apsm.sale_line_id not in ( + select distinct coalesce(posm.sale_line_id,0) + from purchase_order_sales_match posm + where posm.state not in ('cancel') + ) + ) + """ % self._table) diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 7d7c98f4..601f04c5 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -180,3 +180,4 @@ access_reject_reason_commision,reject.reason.commision,model_reject_reason_commi access_shipping_option,shipping.option,model_shipping_option,,1,1,1,1 access_production_purchase_match,access.production.purchase.match,model_production_purchase_match,,1,1,1,1 access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 +access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 -- cgit v1.2.3 From 5d54ea0ad8d3c3a5dc125507122c395ac27a5729 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 7 May 2025 13:42:00 +0700 Subject: push --- indoteknik_custom/models/account_move.py | 13 +++++++++++++ indoteknik_custom/models/sale_order.py | 2 +- indoteknik_custom/views/account_move.xml | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 906985de..30de67be 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -66,6 +66,19 @@ class AccountMove(models.Model): other_taxes = fields.Float(string="Other Taxes", compute='compute_other_taxes') is_hr = fields.Boolean(string="Is HR?", default=False) purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order') + length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment') + + def compute_length_of_payment(self): + for rec in self: + payment_term = rec.invoice_payment_term_id.line_ids[0].days + terima_faktur = rec.date_terima_tukar_faktur + payment = self.search([('ref', '=', rec.name), ('move_type', '=', 'entry')], limit=1) + + if payment and terima_faktur: + date_diff = terima_faktur - payment.date + rec.length_of_payment = date_diff.days + payment_term + else: + rec.length_of_payment = 0 def _update_line_name_from_ref(self): """Update all account.move.line name fields with ref from account.move""" diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index f99058ea..0d4fc6c3 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -297,7 +297,7 @@ class SaleOrder(models.Model): ) nomor_so_pengganti = fields.Char(string='Nomor SO Pengganti', copy=False, tracking=3) shipping_option_id = fields.Many2one("shipping.option", string="Selected Shipping Option", domain="['|', ('sale_order_id', '=', False), ('sale_order_id', '=', id)]") - hold_outgoing = fields.Boolean('Hold Outgoing SO') + hold_outgoing = fields.Boolean('Hold Outgoing SO', tracking=3) state_ask_cancel = fields.Selection([ ('hold', 'Hold'), ('approve', 'Approve') diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index 17263c3a..46737a40 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -92,6 +92,7 @@ + -- cgit v1.2.3 From 71a780abc391d11c2fe2ea16a953eefd2ff74219 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 7 May 2025 13:47:44 +0700 Subject: fix posm query --- indoteknik_custom/models/automatic_purchase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index a39abba9..d619e160 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -699,7 +699,8 @@ class SaleNotInMatchPO(models.Model): where apsm.sale_line_id not in ( select distinct coalesce(posm.sale_line_id,0) from purchase_order_sales_match posm - where posm.state not in ('cancel') + left join purchase_order po on po.id = posm.purchase_order_id + where po.state not in ('cancel') ) ) """ % self._table) -- cgit v1.2.3 From 59079cd2ff5ff82f444566a6d1b62fafcd4c6c44 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 7 May 2025 15:00:08 +0700 Subject: add more tnc --- indoteknik_custom/models/voucher.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index cda9309f..145cd814 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -309,15 +309,24 @@ class Voucher(models.Model): return ' '.join(tnc) def generate_detail_tnc(self): - # def format_currency(amount): - # formatted_number = '{:,.0f}'.format(amount).replace(',', '.') - # return f'Rp{formatted_number}' + def format_currency(amount): + formatted_number = '{:,.0f}'.format(amount).replace(',', '.') + return f'Rp{formatted_number}' tnc = [] + if self.apply_type == 'all': tnc.append('
  • ') tnc.append('Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.') tnc.append('
  • ') + tnc.append('
  • ') + if self.discount_type == 'fixed_price': + tnc.append( + f'Voucher untuk minimal pembelian {format_currency(self.min_purchase_amount)} dengan potongan hingga {format_currency(self.discount_amount)}') + tnc.append('
  • ') + elif self.discount_type == 'percentage': + tnc.append( + f'Voucher untuk minimal pembelian {format_currency(self.min_purchase_amount)} dengan potongan hingga {self.discount_amount}%') elif len(self.voucher_line) > 0: tnc.append( '
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') -- cgit v1.2.3 From 34c21a73b535e8c543deb281d2bf587bfd79afb5 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Wed, 7 May 2025 15:13:51 +0700 Subject: change to join in purchase sales order match --- indoteknik_custom/models/automatic_purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index d619e160..1a1b3a30 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -699,7 +699,7 @@ class SaleNotInMatchPO(models.Model): where apsm.sale_line_id not in ( select distinct coalesce(posm.sale_line_id,0) from purchase_order_sales_match posm - left join purchase_order po on po.id = posm.purchase_order_id + join purchase_order po on po.id = posm.purchase_order_id where po.state not in ('cancel') ) ) -- cgit v1.2.3 From 7b811b7c693d92da0dfe359df8c7caad59194db5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 7 May 2025 16:13:06 +0700 Subject: remove logging and update tnc --- indoteknik_custom/models/voucher.py | 42 +++++++++---------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 145cd814..66e336c9 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -78,33 +78,22 @@ class Voucher(models.Model): return bool(set(public_categories.ids) & set(self.voucher_category.ids)) def is_voucher_applicable_for_category(self, category): - import logging - _logger = logging.getLogger(__name__) - - # If voucher has no category restrictions, it applies to all if not self.voucher_category: - _logger.info("Voucher %s has no category restrictions", self.code) return True - # Check if the product's category directly matches one of the voucher's categories if category.id in self.voucher_category.ids: - _logger.info("Category %s directly matches voucher %s", category.name, self.code) return True - # Build the category hierarchy path for the product's category category_path = [] current_cat = category while current_cat: category_path.append(current_cat.id) current_cat = current_cat.parent_id - # Check if any of the voucher's categories are in the category path (parent categories) for voucher_cat in self.voucher_category: if voucher_cat.id in category_path: - _logger.info("Voucher category %s is in the category path of %s", voucher_cat.name, category.name) return True - _logger.info("No applicable category found for voucher %s and category %s", self.code, category.name) return False @api.constrains('description') @@ -294,15 +283,9 @@ class Voucher(models.Model): '
  • Voucher hanya berlaku apabila pembelian Pengguna sudah memenuhi syarat dan ketentuan yang tertera pada voucher
  • ') tnc.append(f'
  • Voucher berlaku {self._res_remaining_time()} lagi
  • ') tnc.append(f'
  • Voucher tidak bisa digunakan apabila terdapat produk flash sale
  • ') - if self.voucher_category: - category_names = ', '.join([cat.name for cat in self.voucher_category]) - tnc.append( - f'
  • Voucher hanya berlaku untuk produk dalam kategori {category_names} dan sub-kategorinya
  • ') - tnc.append( - f'
  • Voucher tidak dapat digunakan jika ada produk di keranjang yang tidak termasuk dalam kategori tersebut
  • ') - - if len(self.voucher_line) > 0: - tnc.append(f'
  • Voucher berlaku untuk produk dari brand terpilih
  • ') + tnc.append( + '
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') + tnc.append(self.generate_detail_tnc()) tnc.append('') @@ -316,20 +299,15 @@ class Voucher(models.Model): tnc = [] if self.apply_type == 'all': - tnc.append('
  • ') - tnc.append('Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.') - tnc.append('
  • ') - tnc.append('
  • ') - if self.discount_type == 'fixed_price': + if self.voucher_category: + category_names = ', '.join([cat.name for cat in self.voucher_category]) tnc.append( - f'Voucher untuk minimal pembelian {format_currency(self.min_purchase_amount)} dengan potongan hingga {format_currency(self.discount_amount)}') - tnc.append('
  • ') - elif self.discount_type == 'percentage': + f'
  • Voucher hanya berlaku untuk produk dalam kategori {category_names} dan sub-kategorinya
  • ') tnc.append( - f'Voucher untuk minimal pembelian {format_currency(self.min_purchase_amount)} dengan potongan hingga {self.discount_amount}%') - elif len(self.voucher_line) > 0: - tnc.append( - '
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') + f'
  • Voucher tidak dapat digunakan jika ada produk di keranjang yang tidak termasuk dalam kategori tersebut
  • ') + else: + tnc.append(f'
  • Voucher berlaku untuk produk dari brand terpilih
  • ') + return ' '.join(tnc) # def generate_detail_tnc(self): -- cgit v1.2.3 From 8c8cc93b60dfef60342ccd47b440f702bc18c754 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 7 May 2025 16:20:15 +0700 Subject: change trigger unlink note pj and add validation fill sj return date --- indoteknik_custom/models/automatic_purchase.py | 10 ++++++++++ indoteknik_custom/models/purchasing_job_multi_update.py | 2 +- indoteknik_custom/models/stock_picking.py | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index d619e160..ddb4a973 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -67,6 +67,15 @@ class AutomaticPurchase(models.Model): if count > 0: raise UserError('Ada sekitar %s SO Yang sudah create PO, berikut SO nya: %s' % (count, ', '.join(names))) + + def unlink_note_pj(self): + product = self.purchase_lines.mapped('product_id') + pj_state = self.env['purchasing.job.state'].search([ + ('purchasing_job_id', 'in', product.ids) + ]) + + for line in pj_state: + line.unlink() def create_po_from_automatic_purchase(self): if not self.purchase_lines: @@ -75,6 +84,7 @@ class AutomaticPurchase(models.Model): raise UserError('Sudah pernah di create PO') current_time = datetime.now() + self.unlink_note_pj() vendor_ids = self.env['automatic.purchase.line'].read_group( [('automatic_purchase_id', '=', self.id), ('partner_id', '!=', False)], fields=['partner_id'], diff --git a/indoteknik_custom/models/purchasing_job_multi_update.py b/indoteknik_custom/models/purchasing_job_multi_update.py index deba960a..80a43e45 100644 --- a/indoteknik_custom/models/purchasing_job_multi_update.py +++ b/indoteknik_custom/models/purchasing_job_multi_update.py @@ -18,7 +18,7 @@ class PurchasingJobMultiUpdate(models.TransientModel): ('purchasing_job_id', '=', product.id) ]) - purchasing_job_state.unlink() + # purchasing_job_state.unlink() purchasing_job_state.create({ 'purchasing_job_id': product.id, diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f431d817..d032f99f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -276,6 +276,15 @@ class StockPicking(models.Model): last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim') update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD') + @api.constrains('sj_return_date') + def _check_sj_return_date(self): + for record in self: + if not record.driver_arrival_date: + if record.sj_return_date: + raise ValidationError( + _("Anda tidak dapat mengubah Tanggal Pengembalian setelah Tanggal Pengiriman!") + ) + def _check_date_doc_kirim_modification(self): for record in self: if record.last_update_date_doc_kirim and not self.env.context.get('from_button_approve'): -- cgit v1.2.3 From 5bd86996e2af3eb9ce1167a3229cb290cca22e29 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 7 May 2025 16:24:35 +0700 Subject: shipment group --- indoteknik_custom/views/ir_sequence.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 9f980751..97bf40bb 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -65,7 +65,7 @@ Shipment Group shipment.group TRUE - SG/%(year)s/ + SGR/%(year)s/ 5 1 1 -- cgit v1.2.3 From a045b3dc711fb049a78db0e65088a561e0fd9c4a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 8 May 2025 07:11:23 +0700 Subject: update tnc --- indoteknik_custom/models/voucher.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 66e336c9..91d87ddd 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -276,6 +276,10 @@ class Voucher(models.Model): return vouchers def generate_tnc(self): + def format_currency(amount): + formatted_number = '{:,.0f}'.format(amount).replace(',', '.') + return f'Rp{formatted_number}' + tnc = [] tnc.append('
      ') @@ -283,31 +287,28 @@ class Voucher(models.Model): '
    1. Voucher hanya berlaku apabila pembelian Pengguna sudah memenuhi syarat dan ketentuan yang tertera pada voucher
    2. ') tnc.append(f'
    3. Voucher berlaku {self._res_remaining_time()} lagi
    4. ') tnc.append(f'
    5. Voucher tidak bisa digunakan apabila terdapat produk flash sale
    6. ') - tnc.append( - '
    7. Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
    8. ') - - tnc.append(self.generate_detail_tnc()) - tnc.append('
    ') - - return ' '.join(tnc) - - def generate_detail_tnc(self): - def format_currency(amount): - formatted_number = '{:,.0f}'.format(amount).replace(',', '.') - return f'Rp{formatted_number}' - - tnc = [] - if self.apply_type == 'all': + if self.apply_type == 'brand': + tnc.append(f'
  • Voucher berlaku untuk produk dari brand terpilih
  • ') + tnc.append( + f'
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') + elif self.apply_type == 'all': if self.voucher_category: category_names = ', '.join([cat.name for cat in self.voucher_category]) tnc.append( f'
  • Voucher hanya berlaku untuk produk dalam kategori {category_names} dan sub-kategorinya
  • ') tnc.append( f'
  • Voucher tidak dapat digunakan jika ada produk di keranjang yang tidak termasuk dalam kategori tersebut
  • ') - else: - tnc.append(f'
  • Voucher berlaku untuk produk dari brand terpilih
  • ') + if self.discount_type == 'percentage' and self.apply_type != 'brand': + tnc.append( + f'
  • Nominal potongan produk yang bisa didapatkan sebesar {self.max_discount_amount}% dengan minimum pembelian {self.min_purchase_amount}
  • ') + elif self.discount_type == 'percentage' and self.apply_type != 'brand': + tnc.append( + f'
  • Nominal potongan produk yang bisa didapatkan sebesar {format_currency(self.discount_amount)} dengan minimum pembelian {format_currency(self.min_purchase_amount)}
  • ') + + tnc.append('') + # tnc.append(self.generate_detail_tnc()) return ' '.join(tnc) # def generate_detail_tnc(self): -- cgit v1.2.3 From 5f04912a511578a73fe298f154713ade56e933b4 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 8 May 2025 08:14:34 +0700 Subject: update tnc for shipping --- indoteknik_custom/models/voucher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 91d87ddd..b213a039 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -292,7 +292,7 @@ class Voucher(models.Model): tnc.append(f'
  • Voucher berlaku untuk produk dari brand terpilih
  • ') tnc.append( f'
  • Nominal potongan produk yang bisa didapatkan hingga 10 Juta dengan minimum pembelian 10 Ribu.
  • ') - elif self.apply_type == 'all': + elif self.apply_type == 'all' or self.apply_type == 'shipping': if self.voucher_category: category_names = ', '.join([cat.name for cat in self.voucher_category]) tnc.append( -- cgit v1.2.3 From 6b3401c7542e26830d60667fa0dbd78c25cc7be9 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 8 May 2025 09:39:29 +0700 Subject: push --- indoteknik_custom/models/commision.py | 3 +++ indoteknik_custom/models/stock_picking.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index bd4c06a4..788fc0f9 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -278,6 +278,9 @@ class CustomerCommision(models.Model): @api.constrains('commision_amt') def _onchange_commision_amt(self): + """ + Constrain to update commision percent from commision amount + """ if not self.env.context.get('_onchange_commision_amt', True): return diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d032f99f..ccccbcdc 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1248,7 +1248,7 @@ class StockPicking(models.Model): continue invoice = self.env['account.move'].search( - [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel'])], limit=1) + [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), ('move_type', '=', 'out_invoice')], limit=1) if not invoice: continue -- cgit v1.2.3 From bc89a3ae2ac20a53d46d8e50da4a3427f44bd870 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 8 May 2025 13:41:03 +0700 Subject: push --- indoteknik_custom/models/shipment_group.py | 35 ++++++++++++++++++++++++++++++ indoteknik_custom/views/shipment_group.xml | 10 +++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py index df3f1bb4..b7d7ac12 100644 --- a/indoteknik_custom/models/shipment_group.py +++ b/indoteknik_custom/models/shipment_group.py @@ -14,6 +14,13 @@ class ShipmentGroup(models.Model): number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True) shipment_line = fields.One2many('shipment.group.line', 'shipment_id', string='Shipment Group Lines', auto_join=True) partner_id = fields.Many2one('res.partner', string='Customer') + carrier_id = fields.Many2one('delivery.carrier', string='Ekspedisi') + total_colly_line = fields.Float(string='Total Colly', compute='_compute_total_colly_line') + + @api.depends('shipment_line.total_colly') + def _compute_total_colly_line(self): + for rec in self: + rec.total_colly_line = sum(rec.shipment_line.mapped('total_colly')) @api.model def create(self, vals): @@ -35,6 +42,26 @@ class ShipmentGroupLine(models.Model): ('indoteknik', 'Indoteknik'), ('customer', 'Customer') ], string='Shipping Paid by', copy=False) + total_colly = fields.Float(string='Total Colly') + carrier_id = fields.Many2one('delivery.carrier', string='Ekspedisi') + + @api.constrains('picking_id') + def _check_picking_id(self): + for rec in self: + if not rec.picking_id: + continue + + duplicates = self.env['shipment.group.line'].search([ + ('picking_id', '=', rec.picking_id.id), + ('id', '!=', rec.id) + ]) + + if duplicates: + shipment_numbers = duplicates.mapped('shipment_id.number') + raise UserError( + f"Picking {rec.picking_id.name} sudah discan dalam shipment group berikut: {', '.join(shipment_numbers)}! " + "Satu picking hanya boleh dimasukkan dalam satu shipment group." + ) @api.depends('picking_id.state') def _compute_state(self): @@ -63,12 +90,20 @@ class ShipmentGroupLine(models.Model): if self.shipment_id.partner_id and self.shipment_id.partner_id != picking.partner_id: raise UserError('Partner must be same as shipment group') + if self.shipment_id.carrier_id and self.shipment_id.carrier_id != picking.carrier_id: + raise UserError('carrier must be same as shipment group') + self.partner_id = picking.partner_id self.shipping_paid_by = picking.sale_id.shipping_paid_by + self.carrier_id = picking.carrier_id.id + self.total_colly = picking.total_mapping_koli if not self.shipment_id.partner_id: self.shipment_id.partner_id = picking.partner_id + if not self.shipment_id.carrier_id: + self.shipment_id.carrier_id = picking.carrier_id + self.sale_id = picking.sale_id @api.model diff --git a/indoteknik_custom/views/shipment_group.xml b/indoteknik_custom/views/shipment_group.xml index e9eec41b..d2c661ba 100644 --- a/indoteknik_custom/views/shipment_group.xml +++ b/indoteknik_custom/views/shipment_group.xml @@ -7,6 +7,8 @@ + +
    @@ -16,11 +18,9 @@ shipment.group.line - - - - + + @@ -37,6 +37,8 @@
    + +
    -- cgit v1.2.3 From 10d6ffad06f2dd89eca972257c2b9326d002949a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 9 May 2025 13:35:42 +0700 Subject: add partner group by in tree view --- indoteknik_custom/models/commision.py | 47 ++++- indoteknik_custom/views/customer_commision.xml | 234 +++++++++++++------------ 2 files changed, 164 insertions(+), 117 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 788fc0f9..eeaa8efc 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -201,7 +201,8 @@ class CustomerCommision(models.Model): grouped_invoice_number = fields.Char(string='Group Invoice Number', compute='_compute_grouped_numbers') sales_id = fields.Many2one('res.users', string="Sales", tracking=True, default=lambda self: self.env.user, - domain=lambda self: [('groups_id', 'in', self.env.ref('sales_team.group_sale_salesman').id)]) + domain=lambda self: [ + ('groups_id', 'in', self.env.ref('sales_team.group_sale_salesman').id)]) date_approved_sales = fields.Datetime(string="Date Approved Sales", tracking=True) date_approved_marketing = fields.Datetime(string="Date Approved Marketing", tracking=True) @@ -213,6 +214,46 @@ class CustomerCommision(models.Model): position_pimpinan = fields.Char(string="Position Pimpinan", tracking=True) position_accounting = fields.Char(string="Position Accounting", tracking=True) + # get partner ids so it can be grouped by + @api.model + def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): + if 'partner_ids' in groupby: + # Get all records matching the domain + records = self.search(domain) + + # Create groups for each partner + groups = {} + for record in records: + for partner in record.partner_ids: + if partner.id not in groups: + groups[partner.id] = { + 'partner_ids': partner, + 'records': self.env['customer.commision'] + } + groups[partner.id]['records'] |= record + + # Format the result + result = [] + for partner_id, group_data in groups.items(): + partner = group_data['partner_ids'] + record_ids = group_data['records'].ids + + # Create the domain + group_domain = [('id', 'in', record_ids)] + if domain: + group_domain = ['&'] + domain + group_domain + + result.append({ + 'partner_ids': (partner.id, partner.display_name), + 'partner_ids_count': len(record_ids), + '__domain': group_domain, + '__count': len(record_ids), + }) + + return result + + return super(CustomerCommision, self).read_group(domain, fields, groupby, offset, limit, orderby, lazy) + def compute_delivery_amt_text(self): tb = Terbilang() @@ -308,9 +349,9 @@ class CustomerCommision(models.Model): def action_confirm_customer_commision(self): jakarta_tz = pytz.timezone('Asia/Jakarta') now = datetime.now(jakarta_tz) - + now_naive = now.replace(tzinfo=None) - + if not self.status or self.status == 'draft': self.status = 'pengajuan1' elif self.status == 'pengajuan1' and self.env.user.is_sales_manager: diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 9f0e1e8a..37df16ff 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -11,11 +11,12 @@ - + + decoration-success="payment_status == 'payment'" + decoration-danger="payment_status == 'pending'" + widget="badge"/> @@ -47,89 +48,89 @@ customer.commision
    - +
    -
    +
    + + + + + + + + + + + + + +
    +
    + + + + + + + + +
    +
    + + + + + - - - - - - - - - - + + + + + + -
    -
    - - - - - - - - +
    - - - - - - - - - - - - - - - - - - - - -
    - - -
    +
    +
    + +
    + + +
    - + reject.reason.commision.form reject.reason.commision @@ -160,7 +161,12 @@ - + + + + @@ -173,17 +179,17 @@ @@ -217,34 +223,34 @@
    -
    +
    + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - -
    - - -
    +
    + +
    + + +
    @@ -257,16 +263,16 @@ \ No newline at end of file -- cgit v1.2.3 From 81a60ec161deb7eba3072172744276d6e5457f64 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 9 May 2025 15:27:38 +0700 Subject: integration api kgx --- indoteknik_custom/models/stock_picking.py | 74 +++++++++++++++++++++++++++++++ indoteknik_custom/views/stock_picking.xml | 12 +++++ 2 files changed, 86 insertions(+) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ccccbcdc..dd2365ec 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -255,6 +255,13 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + # KGX Section + kgx_pod_photo_url = fields.Char('KGX Photo URL') + kgx_pod_photo = fields.Html('KGX Photo', compute='_compute_kgx_image_html') + kgx_pod_signature = fields.Char('KGX Signature URL') + kgx_pod_receive_time = fields.Datetime('KGX Ata Date') + kgx_pod_receiver = fields.Char('KGX Receiver') + total_koli = fields.Integer(compute='_compute_total_koli', string="Total Koli") total_koli_display = fields.Char(compute='_compute_total_koli_display', string="Total Koli Display") linked_out_picking_id = fields.Many2one('stock.picking', string="Linked BU/OUT", copy=False) @@ -276,6 +283,66 @@ class StockPicking(models.Model): last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim') update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD') + def _get_kgx_awb_number(self): + """Menggabungkan name dan origin untuk membuat AWB Number""" + self.ensure_one() + if not self.name or not self.origin: + return False + return f"{self.name} {self.origin}" + + def _download_pod_photo(self, url): + """Mengunduh foto POD dari URL""" + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + return base64.b64encode(response.content) + except Exception as e: + raise UserError(f"Gagal mengunduh foto POD: {str(e)}") + + def _parse_datetime(self, dt_str): + """Parse datetime string dari format KGX""" + try: + from datetime import datetime + # Hilangkan timezone jika ada masalah parsing + if '+' in dt_str: + dt_str = dt_str.split('+')[0] + return datetime.strptime(dt_str, '%Y-%m-%dT%H:%M:%S') + except ValueError: + return False + + def action_get_kgx_pod(self): + self.ensure_one() + + awb_number = self._get_kgx_awb_number() + if not awb_number: + raise UserError("Nomor AWB tidak dapat dibuat, pastikan picking memiliki name dan origin") + + url = "https://kgx.co.id/get_detail_awb" + headers = {'Content-Type': 'application/json'} + payload = {"params" : {'awb_number': awb_number}} + + try: + response = requests.post(url, headers=headers, data=json.dumps(payload)) + response.raise_for_status() + data = response.json() + + if data.get('result', {}).get('data', []): + pod_data = data['result']['data'][0].get('connote_pod', {}) + photo_url = pod_data.get('photo') + + self.kgx_pod_photo_url = photo_url + self.kgx_pod_signature = pod_data.get('signature') + self.kgx_pod_receiver = pod_data.get('receiver') + self.kgx_pod_receive_time = self._parse_datetime(pod_data.get('timeReceive')) + self.driver_arrival_date = self._parse_datetime(pod_data.get('timeReceive')) + + return data + else: + raise UserError(f"Tidak ditemukan data untuk AWB: {awb_number}") + + except requests.exceptions.RequestException as e: + raise UserError(f"Gagal mengambil data POD: {str(e)}") + @api.constrains('sj_return_date') def _check_sj_return_date(self): for record in self: @@ -454,6 +521,13 @@ class StockPicking(models.Model): else: record.lalamove_image_html = "No image available." + def _compute_kgx_image_html(self): + for record in self: + if record.kgx_pod_photo_url: + record.kgx_pod_photo = f'' + else: + record.kgx_pod_photo = "No image available." + def action_fetch_lalamove_order(self): pickings = self.env['stock.picking'].search([ ('picking_type_code', '=', 'outgoing'), diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index b45debd0..ae77ab9a 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -73,6 +73,11 @@ type="object" attrs="{'invisible': [('carrier_id', '!=', 9)]}" /> +