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 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 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 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 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 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 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 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 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 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 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 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 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 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 8923005630ca26e9863f71c031a28183eb590727 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 7 May 2025 16:55:43 +0700 Subject: (andri) setiap melakukan edit product, history perubahan akan tercatat di log note --- indoteknik_custom/models/product_template.py | 75 ++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index a09570f4..8e475f95 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -388,12 +388,77 @@ class ProductTemplate(models.Model): self.env['token.storage'].create([values]) return values + # simpan data lama + def _collect_old_values(self, vals): + return { + record.id: { + field_name: record[field_name] + for field_name in vals.keys() + if field_name in record._fields + } + for record in self + } + + # log perubahan field + def _log_field_changes(self, vals, old_values): + exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] + + for record in self: + changes = [] + for field_name in vals: + if field_name not in record._fields or field_name in exclude_fields: + continue + + field = record._fields[field_name] + field_label = field.string or field_name + old_value = old_values.get(record.id, {}).get(field_name) + new_value = record[field_name] # nilai setelah write + + def stringify(val): + if val in [None, False]: + return 'None' + if isinstance(field, fields.Selection): + return dict(field.selection).get(val, str(val)) + if isinstance(field, fields.Boolean): + return 'Yes' if val else 'No' + if isinstance(field, fields.Many2one): + return val.name if hasattr(val, 'name') else str(val) + if isinstance(field, fields.Many2many): + records = val if isinstance(val, models.Model) else val + names = [] + if records: + for attr in ['name', 'x_name', 'display_name']: + if hasattr(records[0], attr): + names = records.mapped(attr) + break + if not names: + names = [str(r.id) for r in records] + return ", ".join(names) if names else 'None' + return str(val) + + old_val_str = stringify(old_value) + new_val_str = stringify(new_value) + + if old_val_str != new_val_str: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + + if changes: + message = "Updated:
      %s
    " % "".join(changes) + record.message_post(body=message) + + # simpan data lama dan log perubahan field def write(self, vals): - # for rec in self: - # if rec.id == 224484: - # raise UserError('Tidak dapat mengubah produk sementara') - - return super(ProductTemplate, self).write(vals) + old_values = self._collect_old_values(vals) + result = super().write(vals) + self._log_field_changes(vals, old_values) + return result + + # def write(self, vals): + # # for rec in self: + # # if rec.id == 224484: + # # raise UserError('Tidak dapat mengubah produk sementara') + # self._log_field_changes(vals) + # return super(ProductTemplate, self).write(vals) class ProductProduct(models.Model): _inherit = "product.product" -- 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 75bc2e5226862b8401424942ba464f6e70a03604 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 8 May 2025 09:41:15 +0700 Subject: (andri) add log note update field pada product variant --- indoteknik_custom/models/product_template.py | 83 +++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 8e475f95..99ae73d3 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -418,7 +418,10 @@ class ProductTemplate(models.Model): if val in [None, False]: return 'None' if isinstance(field, fields.Selection): - return dict(field.selection).get(val, str(val)) + selection = field.selection + if callable(selection): + selection = selection(record) + return dict(selection).get(val, str(val)) if isinstance(field, fields.Boolean): return 'Yes' if val else 'No' if isinstance(field, fields.Many2one): @@ -777,6 +780,84 @@ class ProductProduct(models.Model): ], limit=1) return pricelist + # simpan data lama + def _collect_old_values(self, vals): + return { + record.id: { + field_name: record[field_name] + for field_name in vals.keys() + if field_name in record._fields + } + for record in self + } + + # log perubahan field + def _log_field_changes(self, vals, old_values): + exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] + + for record in self: + changes = [] + for field_name in vals: + if field_name not in record._fields or field_name in exclude_fields: + continue + + field = record._fields[field_name] + field_label = field.string or field_name + old_value = old_values.get(record.id, {}).get(field_name) + new_value = record[field_name] + + def stringify(val): + if val in [None, False]: + return 'None' + + if isinstance(field, fields.Selection): + selection = field.selection + if callable(selection): + selection = selection(record) + return dict(selection).get(val, str(val)) + + if isinstance(field, fields.Boolean): + return 'Yes' if val else 'No' + + if isinstance(field, fields.Many2one): + if isinstance(val, int): + rec = self.env[field.comodel_name].browse(val) + return rec.display_name if rec.exists() else str(val) + elif isinstance(val, models.BaseModel): + return val.display_name + return str(val) + + if isinstance(field, fields.Many2many): + records = record[field_name] + names = [] + if records: + for attr in ['name', 'x_name', 'display_name']: + if hasattr(records[0], attr): + names = records.mapped(attr) + break + if not names: + names = [str(r.id) for r in records] + return ", ".join(names) if names else 'None' + + return str(val) + + + old_val_str = stringify(old_value) + new_val_str = stringify(new_value) + + if old_val_str != new_val_str: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + + if changes: + message = "Updated:
      %s
    " % "".join(changes) + record.message_post(body=message) + + # simpan data lama dan log perubahan field + def write(self, vals): + old_values = self._collect_old_values(vals) + result = super().write(vals) + self._log_field_changes(vals, old_values) + return result class OutstandingMove(models.Model): _name = 'v.move.outstanding' -- 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 35ed7159d12a5eb915f4c689535e2e8654b43e63 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 8 May 2025 17:09:56 +0700 Subject: (andri) add inherit mail.thread & mail.activity.mixim untuk log note pada pricelist --- indoteknik_custom/models/product_pricelist.py | 8 +++++--- indoteknik_custom/views/product_pricelist.xml | 6 ++++++ indoteknik_custom/views/product_pricelist_item.xml | 6 ++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py index ea3ee6cf..46d12f4d 100644 --- a/indoteknik_custom/models/product_pricelist.py +++ b/indoteknik_custom/models/product_pricelist.py @@ -3,8 +3,9 @@ from datetime import datetime, timedelta class ProductPricelist(models.Model): - _inherit = 'product.pricelist' - + _name = 'product.pricelist' + _inherit = ['product.pricelist', 'mail.thread', 'mail.activity.mixin'] + is_flash_sale = fields.Boolean(string='Flash Sale', default=False) is_show_program = fields.Boolean(string='Show Program', default=False) banner = fields.Binary(string='Banner') @@ -56,7 +57,8 @@ class ProductPricelist(models.Model): return tier_name class ProductPricelistItem(models.Model): - _inherit = 'product.pricelist.item' + _name = 'product.pricelist.item' + _inherit = ['product.pricelist.item', 'mail.thread', 'mail.activity.mixin'] manufacture_id = fields.Many2one('x_manufactures', string='Manufacture') computed_price = fields.Float(string='Computed Price') \ No newline at end of file diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml index 3c2b8b8d..6a1111cf 100644 --- a/indoteknik_custom/views/product_pricelist.xml +++ b/indoteknik_custom/views/product_pricelist.xml @@ -25,6 +25,12 @@ + +
    + + +
    +
    diff --git a/indoteknik_custom/views/product_pricelist_item.xml b/indoteknik_custom/views/product_pricelist_item.xml index 973ae181..86ee9389 100755 --- a/indoteknik_custom/views/product_pricelist_item.xml +++ b/indoteknik_custom/views/product_pricelist_item.xml @@ -11,6 +11,12 @@ product.pricelist.item + +
    + + +
    +
    -- cgit v1.2.3 From aca89f41cf980e2ea9b7f266d4792be52d2e4aa1 Mon Sep 17 00:00:00 2001 From: AndriFP Date: Thu, 8 May 2025 22:12:07 +0700 Subject: (andri) add log note pada purchase list untuk catat perubahan tiap fields --- indoteknik_custom/models/purchase_pricelist.py | 67 +++++++++++++++++++++++++- indoteknik_custom/views/purchase_pricelist.xml | 7 +++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py index e5b35d7f..6ac74c69 100755 --- a/indoteknik_custom/models/purchase_pricelist.py +++ b/indoteknik_custom/models/purchase_pricelist.py @@ -6,6 +6,7 @@ from pytz import timezone class PurchasePricelist(models.Model): _name = 'purchase.pricelist' _rec_name = 'product_id' + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name', compute="_compute_name") product_id = fields.Many2one('product.product', string="Product", required=True) @@ -120,4 +121,68 @@ class PurchasePricelist(models.Model): rec.sync_pricelist_tier() rec.product_id.product_tmpl_id._create_solr_queue('_sync_price_to_solr') - \ No newline at end of file + + def _collect_old_values(self, vals): + return { + record.id: { + field_name: record[field_name] + for field_name in vals.keys() + if field_name in record._fields + } + for record in self + } + + def _log_field_changes(self, vals, old_values): + exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] + + for record in self: + changes = [] + for field_name in vals: + if field_name not in record._fields or field_name in exclude_fields: + continue + + field = record._fields[field_name] + field_label = field.string or field_name + old_value = old_values.get(record.id, {}).get(field_name) + new_value = record[field_name] + + def stringify(val): + if val in [None, False]: + return 'None' + if isinstance(field, fields.Selection): + selection = field.selection + if callable(selection): + selection = selection(record) + return dict(selection).get(val, str(val)) + if isinstance(field, fields.Boolean): + return 'Yes' if val else 'No' + if isinstance(field, fields.Many2one): + return val.name if hasattr(val, 'name') else str(val) + if isinstance(field, fields.Many2many): + records = val if isinstance(val, models.Model) else val + names = [] + if records: + for attr in ['name', 'x_name', 'display_name']: + if hasattr(records[0], attr): + names = records.mapped(attr) + break + if not names: + names = [str(r.id) for r in records] + return ", ".join(names) if names else 'None' + return str(val) + + old_val_str = stringify(old_value) + new_val_str = stringify(new_value) + + if old_val_str != new_val_str: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + + if changes: + message = "Updated:
      %s
    " % "".join(changes) + record.message_post(body=message) + + def write(self, vals): + old_values = self._collect_old_values(vals) + result = super(PurchasePricelist, self).write(vals) + self._log_field_changes(vals, old_values) + return result \ No newline at end of file diff --git a/indoteknik_custom/views/purchase_pricelist.xml b/indoteknik_custom/views/purchase_pricelist.xml index ca5cd416..409e3b6a 100755 --- a/indoteknik_custom/views/purchase_pricelist.xml +++ b/indoteknik_custom/views/purchase_pricelist.xml @@ -20,6 +20,8 @@ + + @@ -52,6 +54,11 @@ +
    + + + +
    -- cgit v1.2.3 From efba26bf68d142168d5189c6ee2b87c6d8a2299d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 9 May 2025 08:01:57 +0700 Subject: (andri) delete chatter pada product pricelist + item --- indoteknik_custom/models/product_pricelist.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py index 46d12f4d..94a9b239 100644 --- a/indoteknik_custom/models/product_pricelist.py +++ b/indoteknik_custom/models/product_pricelist.py @@ -3,8 +3,7 @@ from datetime import datetime, timedelta class ProductPricelist(models.Model): - _name = 'product.pricelist' - _inherit = ['product.pricelist', 'mail.thread', 'mail.activity.mixin'] + _inherit = 'product.pricelist' is_flash_sale = fields.Boolean(string='Flash Sale', default=False) is_show_program = fields.Boolean(string='Show Program', default=False) @@ -57,8 +56,7 @@ class ProductPricelist(models.Model): return tier_name class ProductPricelistItem(models.Model): - _name = 'product.pricelist.item' - _inherit = ['product.pricelist.item', 'mail.thread', 'mail.activity.mixin'] + _inherit = 'product.pricelist.item' manufacture_id = fields.Many2one('x_manufactures', string='Manufacture') computed_price = fields.Float(string='Computed Price') \ No newline at end of file -- cgit v1.2.3 From 09e83469a30d7907b6b6e605dabda879250ef72d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 9 May 2025 08:06:41 +0700 Subject: (andri) delete chatter pada view product pricelist & item --- indoteknik_custom/views/product_pricelist.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml index 6a1111cf..3c2b8b8d 100644 --- a/indoteknik_custom/views/product_pricelist.xml +++ b/indoteknik_custom/views/product_pricelist.xml @@ -25,12 +25,6 @@ - -
    - - -
    -
    -- cgit v1.2.3 From 0d34982dfd6d644e87733459bf0de5c05aad7cb6 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 9 May 2025 08:06:55 +0700 Subject: (andri) delete chatter pada view product pricelist & item --- indoteknik_custom/views/product_pricelist_item.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indoteknik_custom/views/product_pricelist_item.xml b/indoteknik_custom/views/product_pricelist_item.xml index 86ee9389..973ae181 100755 --- a/indoteknik_custom/views/product_pricelist_item.xml +++ b/indoteknik_custom/views/product_pricelist_item.xml @@ -11,12 +11,6 @@ product.pricelist.item - -
    - - -
    -
    -- cgit v1.2.3 From 0f7e495ae7a28bd897929322e0419de91ecb1675 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 9 May 2025 11:21:46 +0700 Subject: (andri) penyesuaian log note pada purchase pricelist & handle perubahan log note image di product & variant (only notif add/update/delete) --- indoteknik_custom/models/product_template.py | 170 +++++++++++++++---------- indoteknik_custom/models/purchase_pricelist.py | 41 ++++-- 2 files changed, 127 insertions(+), 84 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 99ae73d3..373b8ebd 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -402,6 +402,11 @@ class ProductTemplate(models.Model): # log perubahan field def _log_field_changes(self, vals, old_values): exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] + custom_labels = { + 'image_1920': 'Main Image', + 'image_carousel_lines': 'Carousel Images', + 'product_template_image_ids': 'Extra Product Media', + } for record in self: changes = [] @@ -410,11 +415,11 @@ class ProductTemplate(models.Model): continue field = record._fields[field_name] - field_label = field.string or field_name + field_label = custom_labels.get(field_name, field.string or field_name) old_value = old_values.get(record.id, {}).get(field_name) new_value = record[field_name] # nilai setelah write - def stringify(val): + def stringify(val, field, record): if val in [None, False]: return 'None' if isinstance(field, fields.Selection): @@ -425,25 +430,40 @@ class ProductTemplate(models.Model): if isinstance(field, fields.Boolean): return 'Yes' if val else 'No' if isinstance(field, fields.Many2one): - return val.name if hasattr(val, 'name') else str(val) + if isinstance(val, int): + rec = record.env[field.comodel_name].browse(val) + return rec.display_name if rec.exists() else str(val) + elif isinstance(val, models.BaseModel): + return val.display_name + return str(val) if isinstance(field, fields.Many2many): - records = val if isinstance(val, models.Model) else val - names = [] - if records: - for attr in ['name', 'x_name', 'display_name']: - if hasattr(records[0], attr): - names = records.mapped(attr) - break - if not names: - names = [str(r.id) for r in records] - return ", ".join(names) if names else 'None' + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' + for attr in ['name', 'x_name', 'display_name']: + if hasattr(records[0], attr): + return ", ".join(records.mapped(attr)) + return ", ".join(str(r.id) for r in records) + if isinstance(field, fields.One2many): + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' + return f"{field.comodel_name}({', '.join(str(r.id) for r in records)})" return str(val) - old_val_str = stringify(old_value) - new_val_str = stringify(new_value) + old_val_str = stringify(old_value, field, record) + new_val_str = stringify(new_value, field, record) if old_val_str != new_val_str: - changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + if field_name in custom_labels: + if old_val_str == 'None' and new_val_str != 'None': + changes.append(f"
  • {field_label}: image added
  • ") + elif old_val_str != 'None' and new_val_str == 'None': + changes.append(f"
  • {field_label}: image removed
  • ") + else: + changes.append(f"
  • {field_label}: image updated
  • ") + else: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") if changes: message = "Updated:
      %s
    " % "".join(changes) @@ -793,64 +813,74 @@ class ProductProduct(models.Model): # log perubahan field def _log_field_changes(self, vals, old_values): - exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] - - for record in self: - changes = [] - for field_name in vals: - if field_name not in record._fields or field_name in exclude_fields: - continue - - field = record._fields[field_name] - field_label = field.string or field_name - old_value = old_values.get(record.id, {}).get(field_name) - new_value = record[field_name] + exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited'] + # for image fields, use custom labels + custom_labels = { + 'image_1920': 'Main Image', + 'image_carousel_lines': 'Carousel Images', + 'product_template_image_ids': 'Extra Product Media', + } - def stringify(val): - if val in [None, False]: - return 'None' - - if isinstance(field, fields.Selection): - selection = field.selection - if callable(selection): - selection = selection(record) - return dict(selection).get(val, str(val)) - - if isinstance(field, fields.Boolean): - return 'Yes' if val else 'No' - - if isinstance(field, fields.Many2one): - if isinstance(val, int): - rec = self.env[field.comodel_name].browse(val) - return rec.display_name if rec.exists() else str(val) - elif isinstance(val, models.BaseModel): - return val.display_name - return str(val) - - if isinstance(field, fields.Many2many): - records = record[field_name] - names = [] - if records: + for record in self: + changes = [] + for field_name in vals: + if field_name not in record._fields or field_name in exclude_fields: + continue + + field = record._fields[field_name] + field_label = custom_labels.get(field_name, field.string or field_name) + old_value = old_values.get(record.id, {}).get(field_name) + new_value = record[field_name] # nilai setelah write + + def stringify(val, field, record): + if val in [None, False]: + return 'None' + if isinstance(field, fields.Selection): + selection = field.selection + if callable(selection): + selection = selection(record) + return dict(selection).get(val, str(val)) + if isinstance(field, fields.Boolean): + return 'Yes' if val else 'No' + if isinstance(field, fields.Many2one): + if isinstance(val, int): + rec = record.env[field.comodel_name].browse(val) + return rec.display_name if rec.exists() else str(val) + elif isinstance(val, models.BaseModel): + return val.display_name + return str(val) + if isinstance(field, fields.Many2many): + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' for attr in ['name', 'x_name', 'display_name']: if hasattr(records[0], attr): - names = records.mapped(attr) - break - if not names: - names = [str(r.id) for r in records] - return ", ".join(names) if names else 'None' - - return str(val) - - - old_val_str = stringify(old_value) - new_val_str = stringify(new_value) - - if old_val_str != new_val_str: - changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + return ", ".join(records.mapped(attr)) + return ", ".join(str(r.id) for r in records) + if isinstance(field, fields.One2many): + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' + return f"{field.comodel_name}({', '.join(str(r.id) for r in records)})" + return str(val) - if changes: - message = "Updated:
      %s
    " % "".join(changes) - record.message_post(body=message) + old_val_str = stringify(old_value, field, record) + new_val_str = stringify(new_value, field, record) + + if old_val_str != new_val_str: + if field_name in custom_labels: # handle image field + if old_val_str == 'None' and new_val_str != 'None': + changes.append(f"
  • {field_label}: image added
  • ") + elif old_val_str != 'None' and new_val_str == 'None': + changes.append(f"
  • {field_label}: image removed
  • ") + else: + changes.append(f"
  • {field_label}: image updated
  • ") + else: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + + if changes: + message = "Updated:
      %s
    " % "".join(changes) + record.message_post(body=message) # simpan data lama dan log perubahan field def write(self, vals): diff --git a/indoteknik_custom/models/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py index 6ac74c69..764911cf 100755 --- a/indoteknik_custom/models/purchase_pricelist.py +++ b/indoteknik_custom/models/purchase_pricelist.py @@ -146,33 +146,46 @@ class PurchasePricelist(models.Model): old_value = old_values.get(record.id, {}).get(field_name) new_value = record[field_name] - def stringify(val): + def stringify(val, field, record): if val in [None, False]: return 'None' + # Handle Selection if isinstance(field, fields.Selection): selection = field.selection if callable(selection): selection = selection(record) return dict(selection).get(val, str(val)) + # Handle Boolean if isinstance(field, fields.Boolean): return 'Yes' if val else 'No' + # Handle Many2one if isinstance(field, fields.Many2one): - return val.name if hasattr(val, 'name') else str(val) + if isinstance(val, int): + rec = record.env[field.comodel_name].browse(val) + return rec.display_name if rec.exists() else str(val) + elif isinstance(val, models.BaseModel): + return val.display_name + return str(val) + # Handle Many2many if isinstance(field, fields.Many2many): - records = val if isinstance(val, models.Model) else val - names = [] - if records: - for attr in ['name', 'x_name', 'display_name']: - if hasattr(records[0], attr): - names = records.mapped(attr) - break - if not names: - names = [str(r.id) for r in records] - return ", ".join(names) if names else 'None' + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' + for attr in ['name', 'x_name', 'display_name']: + if hasattr(records[0], attr): + return ", ".join(records.mapped(attr)) + return ", ".join(str(r.id) for r in records) + # Handle One2many + if isinstance(field, fields.One2many): + records = val if isinstance(val, models.BaseModel) else record[field.name] + if not records: + return 'None' + return f"{field.comodel_name}({', '.join(str(r.id) for r in records)})" + # Default case (Char, Float, Integer, etc) return str(val) - old_val_str = stringify(old_value) - new_val_str = stringify(new_value) + old_val_str = stringify(old_value, field, record) + new_val_str = stringify(new_value, field, record) if old_val_str != new_val_str: changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") -- 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 474755658e732235a7855810b993184c23f75386 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 9 May 2025 15:20:00 +0700 Subject: (andri) penyesuaian log note untuk perubahan nilai pada tabel vendor pricelist & product attribute --- indoteknik_custom/models/product_template.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 373b8ebd..e95060e6 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -406,6 +406,8 @@ class ProductTemplate(models.Model): 'image_1920': 'Main Image', 'image_carousel_lines': 'Carousel Images', 'product_template_image_ids': 'Extra Product Media', + 'seller_ids': 'Vendor Pricelist', + 'attribute_line_ids': 'Product Attributes', } for record in self: @@ -457,11 +459,11 @@ class ProductTemplate(models.Model): if old_val_str != new_val_str: if field_name in custom_labels: if old_val_str == 'None' and new_val_str != 'None': - changes.append(f"
  • {field_label}: image added
  • ") + changes.append(f"
  • {field_label}: added
  • ") elif old_val_str != 'None' and new_val_str == 'None': - changes.append(f"
  • {field_label}: image removed
  • ") + changes.append(f"
  • {field_label}: removed
  • ") else: - changes.append(f"
  • {field_label}: image updated
  • ") + changes.append(f"
  • {field_label}: updated
  • ") else: changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") -- 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)]}" /> + - - + + + attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}"/> - - - - - - - - -