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_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 +++++++++++++++++++++++ 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/user_form_merchant.py create mode 100644 indoteknik_custom/models/user_merchant_request.py (limited to 'indoteknik_custom/models') 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 -- 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_custom/models/res_partner.py | 8 ++++++++ indoteknik_custom/models/user_form_merchant.py | 4 ++++ indoteknik_custom/models/user_merchant_request.py | 4 ++++ 3 files changed, 16 insertions(+) (limited to 'indoteknik_custom/models') 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 -- 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') 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') -- 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(-) (limited to 'indoteknik_custom/models') 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_custom/models/user_form_merchant.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') 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_custom/models/user_form_merchant.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') 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_custom/models/user_form_merchant.py | 46 +++++++++++++++++--------- 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'indoteknik_custom/models') 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): -- 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_custom/models/res_partner.py | 17 +++++++++++++++++ indoteknik_custom/models/user_form_merchant.py | 2 +- indoteknik_custom/models/user_merchant_request.py | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') 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 -- 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 +++++- 3 files changed, 122 insertions(+), 20 deletions(-) (limited to 'indoteknik_custom/models') 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 -- cgit v1.2.3 From b00d3caf6cd21ae74872031536ceb2f1ff570316 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 5 May 2025 11:05:13 +0700 Subject: (andri) NPWP di SO menjadi readonly --- indoteknik_custom/models/sale_order.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4c48684d..baa72dc0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -204,7 +204,7 @@ class SaleOrder(models.Model): ('nonpkp', 'Non PKP') ], required=True) sppkp = fields.Char(string="SPPKP", required=True, tracking=True) - npwp = fields.Char(string="NPWP", required=True, tracking=True) + npwp = fields.Char(string="NPWP", required=True, tracking=True, compute='_compute_npwp') purchase_total = fields.Monetary(string='Purchase Total', compute='_compute_purchase_total') voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher', copy=False) applied_voucher_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) @@ -995,6 +995,12 @@ class SaleOrder(models.Model): # return ['&', ('order_line.invoice_lines.move_id.move_type', 'in', ('out_invoice', 'out_refund')), ('order_line.invoice_lines.move_id', operator, value)] + @api.depends('partner_id') + def _compute_npwp(self): + for order in self: + partner = order.partner_id.parent_id or order.partner_id + order.npwp = partner.npwp + @api.onchange('partner_id') def onchange_partner_contact(self): parent_id = self.partner_id.parent_id -- cgit v1.2.3 From 4fc562965d80b3315e58568c42075edf154256dc Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 5 May 2025 11:31:32 +0700 Subject: refactor ask approval retur --- indoteknik_custom/models/stock_picking.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1291737e..0b78706c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -947,6 +947,8 @@ class StockPicking(models.Model): if self.env.user.is_accounting: pick.approval_return_status = 'approved' continue + else: + pick.approval_return_status = 'pengajuan1' action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_stock_return_note_wizard') -- cgit v1.2.3 From 921bbc2f0b5b82945aebc11e96ba3847c6f2904d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 5 May 2025 11:48:57 +0700 Subject: (andri) Make the SPPKP & Customer Type columns read-only in SO and ensure the data (along with NPWP) is connected to the contact --- indoteknik_custom/models/sale_order.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index baa72dc0..0711e33a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -202,9 +202,9 @@ class SaleOrder(models.Model): customer_type = fields.Selection([ ('pkp', 'PKP'), ('nonpkp', 'Non PKP') - ], required=True) - sppkp = fields.Char(string="SPPKP", required=True, tracking=True) - npwp = fields.Char(string="NPWP", required=True, tracking=True, compute='_compute_npwp') + ], required=True, compute='_compute_partner_field') + sppkp = fields.Char(string="SPPKP", required=True, tracking=True, compute='_compute_partner_field') + npwp = fields.Char(string="NPWP", required=True, tracking=True, compute='_compute_partner_field') purchase_total = fields.Monetary(string='Purchase Total', compute='_compute_purchase_total') voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher', copy=False) applied_voucher_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) @@ -994,12 +994,13 @@ class SaleOrder(models.Model): # return [('id', 'not in', order_ids)] # return ['&', ('order_line.invoice_lines.move_id.move_type', 'in', ('out_invoice', 'out_refund')), ('order_line.invoice_lines.move_id', operator, value)] - @api.depends('partner_id') - def _compute_npwp(self): + def _compute_partner_field(self): for order in self: partner = order.partner_id.parent_id or order.partner_id - order.npwp = partner.npwp + order.npwp = partner.npwp + order.sppkp = partner.sppkp + order.customer_type = partner.customer_type @api.onchange('partner_id') def onchange_partner_contact(self): -- cgit v1.2.3 From fcc0596b649ef53a6280530b60c89a2a0dc0ebec Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 5 May 2025 17:01:05 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0b78706c..fadcc3c2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1203,7 +1203,7 @@ class StockPicking(models.Model): if not invoice: continue - if not picking.so_lama and (not picking.date_doc_kirim or not invoice.invoice_date): + if not picking.so_lama and invoice and (not picking.date_doc_kirim or not invoice.invoice_date): raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!") picking_date = fields.Date.to_date(picking.date_doc_kirim) -- cgit v1.2.3 From 5b78ec72f001ad0d5ccde1269d1ec86418fb7339 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 5 May 2025 17:02:16 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 47 ++++++++++++++++++++++++++++++++++ indoteknik_custom/models/stock_move.py | 1 + 2 files changed, 48 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b0e17a3a..8af34c75 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -247,6 +247,51 @@ 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') + state_ask_cancel = fields.Selection([ + ('hold', 'Hold'), + ('approve', 'Approve') + ], tracking=True, string='State Cancel', copy=False) + + def ask_retur_cancel_purchasing(self): + for rec in self: + if self.env.user.has_group('indoteknik_custom.group_role_purchasing'): + rec.state_ask_cancel = 'approve' + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Persetujuan Diberikan', + 'message': 'Proses cancel sudah disetujui', + 'type': 'success', + 'sticky': True + } + } + else: + rec.state_ask_cancel = 'hold' + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Menunggu Persetujuan', + 'message': 'Tim Purchasing akan memproses permintaan Anda', + 'type': 'warning', + 'sticky': False + } + } + + def hold_unhold_qty_outgoing_so(self): + pickings = self.env['stock.picking'].search([('sale_id', '=', self.id), ('state', 'not in', ['cancel','done']), ('name', 'ilike', 'BU/PICK/%')]) + products = self.order_line.mapped('product_id') + for picking in pickings: + for line in picking.move_ids_without_package: + if line.product_id in products: + if line.hold_outgoingg == True: + line.hold_outgoingg = False + self.hold_outgoing = False + else: + line.hold_outgoingg = True + self.hold_outgoing = True def _validate_uniform_taxes(self): for order in self: @@ -1392,6 +1437,8 @@ class SaleOrder(models.Model): def action_cancel(self): # TODO stephan prevent cancel if have invoice, do, and po + if self.state_ask_cancel != 'approve': + raise UserError("Anda harus approval purchasing terlebih dahulu") main_parent = self.partner_id.get_main_parent() if self._name != 'sale.order': return super(SaleOrder, self).action_cancel() diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 3c765244..90ab30a4 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -14,6 +14,7 @@ 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') + hold_outgoingg = fields.Boolean('Hold Outgoing', default=False) # @api.model_create_multi # def create(self, vals_list): -- cgit v1.2.3 From e16c4fd64143db43eff45d39789a1e59d64f1d34 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 6 May 2025 09:19:46 +0700 Subject: push --- indoteknik_custom/models/solr/product_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py index 20402c84..c4aefe19 100644 --- a/indoteknik_custom/models/solr/product_template.py +++ b/indoteknik_custom/models/solr/product_template.py @@ -100,7 +100,7 @@ class ProductTemplate(models.Model): "product_rating_f": template.virtual_rating, "product_id_i": template.id, "image_s": self.env['ir.attachment'].api_image('product.template', 'image_512', template.id), - "image_carousel_ss": carousel_images, + "image_carousel_ss": carousel_images if carousel_images else [], 'image_mobile_s': self.env['ir.attachment'].api_image('product.template', 'image_256', template.id), "variant_total_i": template.product_variant_count, "stock_total_f": template.qty_stock_vendor, -- cgit v1.2.3 From 08fb1ce211c3bc11e6ff0ea6ff37e0be8b7c2b31 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 6 May 2025 13:55:37 +0700 Subject: qty hold outgoing and ask cancel purchasing --- indoteknik_custom/models/sale_order.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8af34c75..aa4ece13 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -281,17 +281,10 @@ class SaleOrder(models.Model): } def hold_unhold_qty_outgoing_so(self): - pickings = self.env['stock.picking'].search([('sale_id', '=', self.id), ('state', 'not in', ['cancel','done']), ('name', 'ilike', 'BU/PICK/%')]) - products = self.order_line.mapped('product_id') - for picking in pickings: - for line in picking.move_ids_without_package: - if line.product_id in products: - if line.hold_outgoingg == True: - line.hold_outgoingg = False - self.hold_outgoing = False - else: - line.hold_outgoingg = True - self.hold_outgoing = True + if self.hold_outgoing == True: + self.hold_outgoing = False + else: + self.hold_outgoing = True def _validate_uniform_taxes(self): for order in self: -- cgit v1.2.3 From 2bb2ead5a6ee0a76a088f7e522cabc74ac8809be Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 6 May 2025 15:30:04 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 40 ++++++++++++++++++++--------- indoteknik_custom/models/sale_order_line.py | 19 ++++++++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 1e744f18..0d86019e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -133,8 +133,9 @@ class SaleOrder(models.Model): reject_line = fields.One2many('sales.order.reject', 'sale_order_id', string='Reject Lines') order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', string='Purchase Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True) total_margin = fields.Float('Total Margin', compute='_compute_total_margin', help="Total Margin in Sales Order Header") + total_before_margin = fields.Float('Total Before Margin', compute='_compute_total_before_margin', help="Total Margin in Sales Order Header") total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', help="Total % Margin in Sales Order Header") - total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header") + total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header", compute='_compute_total_margin_excl_third_party') approval_status = fields.Selection([ ('pengajuan1', 'Approval Manager'), ('pengajuan2', 'Approval Pimpinan'), @@ -302,6 +303,16 @@ class SaleOrder(models.Model): ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) + def _compute_total_margin_excl_third_party(self): + for order in self: + if order.amount_untaxed == 0: + order.total_margin_excl_third_party = 0 + continue + + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) + order.total_margin_excl_third_party = round((order.total_before_margin / (order.amount_untaxed)) * 100, 2) + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) + def ask_retur_cancel_purchasing(self): for rec in self: if self.env.user.has_group('indoteknik_custom.group_role_purchasing'): @@ -345,17 +356,17 @@ class SaleOrder(models.Model): if len(tax_sets) > 1: raise ValidationError("Semua produk dalam Sales Order harus memiliki kombinasi pajak yang sama.") - @api.constrains('fee_third_party', 'delivery_amt', 'biaya_lain_lain') - def _check_total_margin_excl_third_party(self): - for rec in self: - if rec.fee_third_party == 0 and rec.total_margin_excl_third_party != rec.total_percent_margin: - # Gunakan direct SQL atau flag context untuk menghindari rekursi - self.env.cr.execute(""" - UPDATE sale_order - SET total_margin_excl_third_party = %s - WHERE id = %s - """, (rec.total_percent_margin, rec.id)) - self.invalidate_cache() + # @api.constrains('fee_third_party', 'delivery_amt', 'biaya_lain_lain') + # def _check_total_margin_excl_third_party(self): + # for rec in self: + # if rec.fee_third_party == 0 and rec.total_margin_excl_third_party != rec.total_percent_margin: + # # Gunakan direct SQL atau flag context untuk menghindari rekursi + # self.env.cr.execute(""" + # UPDATE sale_order + # SET total_margin_excl_third_party = %s + # WHERE id = %s + # """, (rec.total_percent_margin, rec.id)) + # self.invalidate_cache() @api.constrains('shipping_option_id') def _check_shipping_option(self): @@ -1607,6 +1618,11 @@ class SaleOrder(models.Model): order.total_margin = total_margin + def _compute_total_before_margin(self): + for order in self: + total_before_margin = sum(line.item_before_margin for line in order.order_line if line.product_id) + order.total_before_margin = total_before_margin + def _compute_total_percent_margin(self): for order in self: if order.amount_untaxed == 0: diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index aed95aab..2450abd4 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -6,6 +6,7 @@ from datetime import datetime, timedelta class SaleOrderLine(models.Model): _inherit = 'sale.order.line' item_margin = fields.Float('Margin', compute='compute_item_margin', help="Total Margin in Sales Order Header") + item_before_margin = fields.Float('Before Margin', compute='compute_item_before_margin', help="Total Margin in Sales Order Header") item_percent_margin = fields.Float('%Margin', compute='compute_item_margin', help="Total % Margin in Sales Order Header") initial_discount = fields.Float('Initial Discount') vendor_id = fields.Many2one( @@ -146,6 +147,24 @@ class SaleOrderLine(models.Model): if not line.margin_md: line.margin_md = line.item_percent_margin + def compute_item_before_margin(self): + for line in self: + if not line.product_id or line.product_id.type == 'service' \ + or line.price_unit <= 0 or line.product_uom_qty <= 0 \ + or not line.vendor_id: + line.item_before_margin = 0 + continue + # calculate margin without tax + sales_price = line.price_reduce_taxexcl * line.product_uom_qty + + purchase_price = line.purchase_price + if line.purchase_tax_id.price_include: + purchase_price = line.purchase_price / 1.11 + + purchase_price = purchase_price * line.product_uom_qty + margin_per_item = sales_price - purchase_price + line.item_before_margin = margin_per_item + @api.onchange('vendor_id') def onchange_vendor_id(self): # TODO : need to change this logic @stephan -- 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(-) (limited to 'indoteknik_custom/models') 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 ++++++++------- 2 files changed, 213 insertions(+), 115 deletions(-) (limited to 'indoteknik_custom/models') 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(',', '.') -- 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(-) (limited to 'indoteknik_custom/models') 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 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') 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: -- 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(-) (limited to 'indoteknik_custom/models') 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 +++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') 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) -- 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 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') 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') -- 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index bd4c06a4..788fc0f9 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -278,6 +278,9 @@ class CustomerCommision(models.Model): @api.constrains('commision_amt') def _onchange_commision_amt(self): + """ + Constrain to update commision percent from commision amount + """ if not self.env.context.get('_onchange_commision_amt', True): return diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d032f99f..ccccbcdc 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1248,7 +1248,7 @@ class StockPicking(models.Model): continue invoice = self.env['account.move'].search( - [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel'])], limit=1) + [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), ('move_type', '=', 'out_invoice')], limit=1) if not invoice: continue -- cgit v1.2.3 From bc89a3ae2ac20a53d46d8e50da4a3427f44bd870 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 8 May 2025 13:41:03 +0700 Subject: push --- indoteknik_custom/models/shipment_group.py | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'indoteknik_custom/models') 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 -- 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 ++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') 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: -- 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 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'indoteknik_custom/models') 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'), -- cgit v1.2.3 From f134f03f0e9998001a6fec44502a1e5a0a6b821b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 13 May 2025 11:13:36 +0700 Subject: fix bug return, and change view project --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index dd2365ec..6a6fe352 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -87,7 +87,7 @@ class StockPicking(models.Model): ) sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan", ) paket_documentation = fields.Binary(string="Dokumentasi Paket", ) - sj_return_date = fields.Datetime(string="SJ Return Date", ) + sj_return_date = fields.Datetime(string="SJ Return Date", copy=False) responsible = fields.Many2one('res.users', string='Responsible', tracking=True) approval_status = fields.Selection([ -- cgit v1.2.3 From 2469ee37cfe854f0419a8c3fbabed5bc32bcaa6e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 13 May 2025 14:56:55 +0700 Subject: push --- indoteknik_custom/models/purchase_order_sales_match.py | 1 + indoteknik_custom/models/purchase_pricelist.py | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order_sales_match.py b/indoteknik_custom/models/purchase_order_sales_match.py index ed013dd5..0bd0092b 100644 --- a/indoteknik_custom/models/purchase_order_sales_match.py +++ b/indoteknik_custom/models/purchase_order_sales_match.py @@ -27,6 +27,7 @@ class PurchaseOrderSalesMatch(models.Model): purchase_price_so = fields.Float(string='Purchase Price Sale Order', related='sale_line_id.purchase_price') purchase_price_po = fields.Float('Purchase Price PO', compute='_compute_purchase_price_po') purchase_line_id = fields.Many2one('purchase.order.line', string='Purchase Line', compute='_compute_purchase_line_id') + hold_outgoing_so = fields.Boolean(string='Hold Outgoing SO', related='sale_id.hold_outgoing') def _compute_purchase_line_id(self): for line in self: diff --git a/indoteknik_custom/models/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py index e5b35d7f..dd680f4d 100755 --- a/indoteknik_custom/models/purchase_pricelist.py +++ b/indoteknik_custom/models/purchase_pricelist.py @@ -83,6 +83,15 @@ class PurchasePricelist(models.Model): massage="Ada duplikat product dan vendor, berikut data yang anda duplikat : \n" + str(existing_purchase.product_id.name) + " - " + str(existing_purchase.vendor_id.name) + " - " + str(existing_purchase.product_price) if existing_purchase: raise UserError(massage) + + def sync_pricelist_item_promo(self, product): + pricelist_product = self.env['product.pricelist.item'].search([('product_id', '=', product.id), ('pricelist_id', '=', 17022)]) + for pricelist in pricelist_product: + if pricelist.fixed_price == 0: + flashsale = self.env['product.pricelist.item'].search([('product_id', '=', product.id), ('pricelist_id.is_flash_sale', '=', True)]) + if flashsale: + flashsale.fixed_price = 0 + return def action_calculate_pricelist(self): MAX_PRICELIST = 10 @@ -94,6 +103,8 @@ class PurchasePricelist(models.Model): records = self.env['purchase.pricelist'].browse(active_ids) price_group = self.env['price.group'].collect_price_group() for rec in records: + if rec.include_price == 0: + rec.sync_pricelist_item_promo(rec.product_id) product_group = rec.product_id.product_tmpl_id.x_manufacture.pricing_group or None price_incl = rec.include_price -- cgit v1.2.3 From eeb873f69948315e214082315cfeedfe6c7536bf Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 14 May 2025 09:46:22 +0700 Subject: add notes in sales order --- indoteknik_custom/models/sale_order.py | 466 ++++++++++++++++++--------------- 1 file changed, 259 insertions(+), 207 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0d4fc6c3..99aa8053 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -9,6 +9,7 @@ from collections import defaultdict _logger = logging.getLogger(__name__) + class CancelReasonOrder(models.TransientModel): _name = 'cancel.reason.order' _description = 'Wizard for Cancel Reason order' @@ -44,7 +45,7 @@ class CancelReasonOrder(models.TransientModel): raise UserError('Attachment bukti wajib disertakan') order.write({'attachment_bukti': self.attachment_bukti}) order.message_post(body='Attachment Bukti Cancel', - attachment_ids=[self.attachment_bukti.id]) + attachment_ids=[self.attachment_bukti.id]) if self.reason_cancel == 'ganti_quotation': if self.nomor_so_pengganti: order.write({'nomor_so_pengganti': self.nomor_so_pengganti}) @@ -53,7 +54,8 @@ class CancelReasonOrder(models.TransientModel): order.confirm_cancel_order() return {'type': 'ir.actions.act_window_close'} - + + class ShippingOption(models.Model): _name = "shipping.option" _description = "Shipping Option" @@ -64,6 +66,7 @@ class ShippingOption(models.Model): etd = fields.Char(string="Estimated Delivery Time") sale_order_id = fields.Many2one('sale.order', string="Sale Order", ondelete="cascade") + class SaleOrderLine(models.Model): _inherit = 'sale.order.line' @@ -73,7 +76,7 @@ class SaleOrderLine(models.Model): if line.order_id: now = fields.Datetime.now() - initial_reason="Product Rejected" + initial_reason = "Product Rejected" # Buat lognote untuk product yang di delete log_note = (f"
  • Product '{line.product_id.name}' rejected.
  • " @@ -113,10 +116,12 @@ class SaleOrderLine(models.Model): return result + class SaleOrder(models.Model): _inherit = "sale.order" - ongkir_ke_xpdc = fields.Float(string='Ongkir ke Ekspedisi', help='Biaya ongkir ekspedisi', copy=False, index=True, tracking=3) + ongkir_ke_xpdc = fields.Float(string='Ongkir ke Ekspedisi', help='Biaya ongkir ekspedisi', copy=False, index=True, + tracking=3) metode_kirim_ke_xpdc = fields.Selection([ ('indoteknik_deliv', 'Indoteknik Delivery'), @@ -127,22 +132,31 @@ class SaleOrder(models.Model): ('other', 'Other'), ], string='Metode Kirim Ke Ekspedisi', copy=False, index=True, tracking=3) + notes = fields.Text(string="Notes", tracking=3) 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') - order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', string='Purchase Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True) - total_margin = fields.Float('Total Margin', compute='_compute_total_margin', help="Total Margin in Sales Order Header") - total_before_margin = fields.Float('Total Before Margin', compute='_compute_total_before_margin', help="Total Margin in Sales Order Header") - total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', help="Total % Margin in Sales Order Header") - total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header", compute='_compute_total_margin_excl_third_party') + order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', + string='Purchase Match Lines', + states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, + copy=True) + total_margin = fields.Float('Total Margin', compute='_compute_total_margin', + help="Total Margin in Sales Order Header") + total_before_margin = fields.Float('Total Before Margin', compute='_compute_total_before_margin', + help="Total Margin in Sales Order Header") + total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', + help="Total % Margin in Sales Order Header") + total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header", + compute='_compute_total_margin_excl_third_party') approval_status = fields.Selection([ ('pengajuan1', 'Approval Manager'), ('pengajuan2', 'Approval Pimpinan'), ('approved', 'Approved'), ], string='Approval Status', readonly=True, copy=False, index=True, tracking=3) carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method', tracking=3) - have_visit_service = fields.Boolean(string='Have Visit Service', compute='_have_visit_service', help='To compute is customer get visit service') + have_visit_service = fields.Boolean(string='Have Visit Service', compute='_have_visit_service', + help='To compute is customer get visit service') delivery_amt = fields.Float(string='Delivery Amt', copy=False) shipping_cost_covered = fields.Selection([ ('indoteknik', 'Indoteknik'), @@ -152,7 +166,8 @@ class SaleOrder(models.Model): ('indoteknik', 'Indoteknik'), ('customer', 'Customer') ], string='Shipping Paid by', help='Siapa yang talangin dulu Biaya ekspedisi-nya?', copy=False, tracking=3) - sales_tax_id = fields.Many2one('account.tax', string='Tax', domain=['|', ('active', '=', False), ('active', '=', True)]) + sales_tax_id = fields.Many2one('account.tax', string='Tax', + domain=['|', ('active', '=', False), ('active', '=', True)]) have_outstanding_invoice = fields.Boolean('Have Outstanding Invoice', compute='_have_outstanding_invoice') have_outstanding_picking = fields.Boolean('Have Outstanding Picking', compute='_have_outstanding_picking') have_outstanding_po = fields.Boolean('Have Outstanding PO', compute='_have_outstanding_po') @@ -173,9 +188,14 @@ class SaleOrder(models.Model): ('sebagian', 'Sebagian Diproses'), ('menunggu', 'Menunggu Diproses'), ], copy=False) - partner_purchase_order_name = fields.Char(string='Nama PO Customer', copy=False, help="Nama purchase order customer, diisi oleh customer melalui website.", tracking=3) - partner_purchase_order_description = fields.Text(string='Keterangan PO Customer', copy=False, help="Keterangan purchase order customer, diisi oleh customer melalui website.", tracking=3) - partner_purchase_order_file = fields.Binary(string='File PO Customer', copy=False, help="File purchase order customer, diisi oleh customer melalui website.") + partner_purchase_order_name = fields.Char(string='Nama PO Customer', copy=False, + help="Nama purchase order customer, diisi oleh customer melalui website.", + tracking=3) + partner_purchase_order_description = fields.Text(string='Keterangan PO Customer', copy=False, + help="Keterangan purchase order customer, diisi oleh customer melalui website.", + tracking=3) + partner_purchase_order_file = fields.Binary(string='File PO Customer', copy=False, + help="File purchase order customer, diisi oleh customer melalui website.") payment_status = fields.Selection([ ('pending', 'Pending'), ('capture', 'Capture'), @@ -189,17 +209,22 @@ class SaleOrder(models.Model): ('partial_refund', 'Partial Refund'), ('partial_chargeback', 'Partial Chargeback'), ('authorize', 'Authorize'), - ], tracking=True, string='Payment Status', help='Payment Gateway Status / Midtrans / Web, https://docs.midtrans.com/en/after-payment/status-cycle') - date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ yang terakhir, tidak berpengaruh ke Accounting") + ], tracking=True, string='Payment Status', + help='Payment Gateway Status / Midtrans / Web, https://docs.midtrans.com/en/after-payment/status-cycle') + date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', + help="Tanggal Kirim di cetakan SJ yang terakhir, tidak berpengaruh ke Accounting") payment_type = fields.Char(string='Payment Type', help='Jenis pembayaran dengan Midtrans') gross_amount = fields.Float(string='Gross Amount', help='Jumlah pembayaran yang dilakukan dengan Midtrans') notification = fields.Char(string='Notification', help='Dapat membantu error dari approval') delivery_service_type = fields.Char(string='Delivery Service Type', help='data dari rajaongkir') - grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total') - payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri') + grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', + compute='_compute_grand_total') + payment_link_midtrans = fields.Char(string='Payment Link', + help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri') payment_qr_code = fields.Binary("Payment QR Code") - due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True) - vendor_approval_id = fields.Many2many('vendor.approval', string="Vendor Approval", readonly=True, tracking=True, copy=False) + due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True) + vendor_approval_id = fields.Many2many('vendor.approval', string="Vendor Approval", readonly=True, tracking=True, + copy=False) customer_type = fields.Selection([ ('pkp', 'PKP'), ('nonpkp', 'Non PKP') @@ -239,8 +264,10 @@ class SaleOrder(models.Model): use_button = fields.Boolean(string='Using Calculate Selling Price', copy=False) unreserve_id = fields.Many2one('stock.picking', 'Unreserve Picking') voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Voucher Shipping', copy=False) - margin_after_delivery_purchase = fields.Float(string='Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase') - percent_margin_after_delivery_purchase = fields.Float(string='% Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase') + margin_after_delivery_purchase = fields.Float(string='Margin After Delivery Purchase', + compute='_compute_margin_after_delivery_purchase') + percent_margin_after_delivery_purchase = fields.Float(string='% Margin After Delivery Purchase', + compute='_compute_margin_after_delivery_purchase') purchase_delivery_amt = fields.Float(string='Purchase Delivery Amount', compute='_compute_purchase_delivery_amount') type_promotion = fields.Char(string='Type Promotion', compute='_compute_type_promotion') partner_invoice_id = fields.Many2one( @@ -254,11 +281,11 @@ class SaleOrder(models.Model): 'res.partner', string='Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]}, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) - + payment_term_id = fields.Many2one( 'account.payment.term', string='Payment Terms', check_company=True, # Unrequired company domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) - + total_weight = fields.Float(string='Total Weight', compute='_compute_total_weight') pareto_status = fields.Selection([ ('PR', 'Pareto Repeating'), @@ -275,7 +302,7 @@ class SaleOrder(models.Model): copy=False ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') - + reason_cancel = fields.Selection([ ('harga_terlalu_mahal', 'Harga barang terlalu mahal'), ('harga_web_tidak_valid', 'Harga web tidak valid'), @@ -296,7 +323,8 @@ class SaleOrder(models.Model): string="Attachment Bukti Cancel", readonly=False, ) nomor_so_pengganti = fields.Char(string='Nomor SO Pengganti', copy=False, tracking=3) - shipping_option_id = fields.Many2one("shipping.option", string="Selected Shipping Option", domain="['|', ('sale_order_id', '=', False), ('sale_order_id', '=', id)]") + 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', tracking=3) state_ask_cancel = fields.Selection([ ('hold', 'Hold'), @@ -355,7 +383,7 @@ class SaleOrder(models.Model): tax_sets.add(tax_ids) if len(tax_sets) > 1: raise ValidationError("Semua produk dalam Sales Order harus memiliki kombinasi pajak yang sama.") - + # @api.constrains('fee_third_party', 'delivery_amt', 'biaya_lain_lain') # def _check_total_margin_excl_third_party(self): # for rec in self: @@ -403,14 +431,14 @@ class SaleOrder(models.Model): def action_indoteknik_estimate_shipping(self): if not self.real_shipping_id.kota_id.is_jabodetabek: raise UserError('Estimasi ongkir hanya bisa dilakukan di kota Jabodetabek') - + total_weight = 0 missing_weight_products = [] for line in self.order_line: if line.weight > 0: total_weight += line.weight * line.product_uom_qty - line.product_id.weight = line.weight + line.product_id.weight = line.weight else: missing_weight_products.append(line.product_id.name) @@ -420,10 +448,10 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - + if total_weight < 10: total_weight = 10 - + self.delivery_amt = total_weight * 3000 shipping_option = self.env["shipping.option"].create({ @@ -456,7 +484,7 @@ class SaleOrder(models.Model): for line in self.order_line: if line.weight > 0: total_weight += line.weight * line.product_uom_qty - line.product_id.weight = line.weight + line.product_id.weight = line.weight else: missing_weight_products.append(line.product_id.name) @@ -481,11 +509,11 @@ class SaleOrder(models.Model): etd = cost_detail['cost'][0]['etd'] value = cost_detail['cost'][0]['value'] shipping_options.append((service, description, etd, value, courier['code'])) - + self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink() _logger.info(f"Shipping options: {shipping_options}") - + for service, description, etd, value, provider in shipping_options: self.env["shipping.option"].create({ "name": service, @@ -495,22 +523,20 @@ class SaleOrder(models.Model): "sale_order_id": self.id, }) - self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id _logger.info(f"Shipping option SO ID: {self.shipping_option_id}") self.message_post( body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
    Detail Lain:
    " - f"{'
    '.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", + f"{'
    '.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment" ) - + # self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
    Detail Lain:
    {'
    '.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment") else: raise UserError("Gagal mendapatkan estimasi ongkir.") - def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id): url = 'https://pro.rajaongkir.com/api/cost' @@ -532,7 +558,7 @@ class SaleOrder(models.Model): if response.status_code == 200: return response.json() return None - + def _normalize_city_name(self, city_name): city_name = city_name.lower() @@ -552,7 +578,7 @@ class SaleOrder(models.Model): } normalized_city_name = self._normalize_city_name(city_name) - + response = requests.get(url, headers=headers) if response.status_code == 200: city_data = response.json() @@ -566,7 +592,7 @@ class SaleOrder(models.Model): headers = { 'key': '9b1310f644056d84d60b0af6bb21611a', } - + response = requests.get(url, headers=headers) if response.status_code == 200: subdistrict_data = response.json() @@ -578,15 +604,15 @@ class SaleOrder(models.Model): return subdistrict['subdistrict_id'] return None - def _compute_type_promotion(self): for rec in self: promotion_types = [] for promotion in rec.order_promotion_ids: for line_program in promotion.program_line_id: if line_program.promotion_type: - promotion_types.append(dict(line_program._fields['promotion_type'].selection).get(line_program.promotion_type)) - + promotion_types.append( + dict(line_program._fields['promotion_type'].selection).get(line_program.promotion_type)) + rec.type_promotion = ', '.join(sorted(set(promotion_types))) def _compute_purchase_delivery_amount(self): @@ -610,11 +636,14 @@ class SaleOrder(models.Model): delivery_amt = order.delivery_amt else: delivery_amt = 0 - order.percent_margin_after_delivery_purchase = round((order.margin_after_delivery_purchase / (order.amount_untaxed-delivery_amt-order.fee_third_party-order.biaya_lain_lain)) * 100, 2) + order.percent_margin_after_delivery_purchase = round((order.margin_after_delivery_purchase / ( + order.amount_untaxed - delivery_amt - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) def _compute_date_kirim(self): for rec in self: - picking = self.env['stock.picking'].search([('sale_id', '=', rec.id), ('state', 'not in', ['cancel']), ('name', 'not ilike', 'BU/PICK/%')], order='date_doc_kirim desc', limit=1) + picking = self.env['stock.picking'].search( + [('sale_id', '=', rec.id), ('state', 'not in', ['cancel']), ('name', 'not ilike', 'BU/PICK/%')], + order='date_doc_kirim desc', limit=1) rec.date_kirim_ril = picking.date_doc_kirim rec.date_status_done = picking.date_done rec.date_driver_arrival = picking.driver_arrival_date @@ -626,54 +655,55 @@ class SaleOrder(models.Model): 'so_ids': [x.id for x in self] } return action - + def _compute_fullfillment(self): for rec in self: - # rec.fullfillment_line.unlink() - # - # for line in rec.order_line: - # line._compute_reserved_from() + # rec.fullfillment_line.unlink() + # + # for line in rec.order_line: + # line._compute_reserved_from() rec.compute_fullfillment = True @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: + 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 = 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 - - - def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): + + def get_days_until_next_business_day(self, start_date=None, *args, **kwargs): today = start_date or datetime.today().date() offset = 0 # Counter jumlah hari yang ditambahkan holiday = self.env['hr.public.holiday'] - while True : + while True: today += timedelta(days=1) offset += 1 - + if today.weekday() >= 5: continue is_holiday = holiday.search([("start_date", "=", today)]) if is_holiday: continue - + break return offset - + def calculate_sla_by_vendor(self, products): product_ids = products.mapped('product_id.id') # Kumpulkan semua ID produk include_instant = True # Default True, tetapi bisa menjadi False # Cek apakah SEMUA produk memiliki qty_free_bandengan >= qty_needed - all_fast_products = all(product.product_id.qty_free_bandengan >= product.product_uom_qty for product in products) + all_fast_products = all( + product.product_id.qty_free_bandengan >= product.product_uom_qty for product in products) if all_fast_products: return {'slatime': 1, 'include_instant': include_instant} @@ -683,7 +713,7 @@ class SaleOrder(models.Model): ('is_winner', '=', True) ]) - max_slatime = 1 + max_slatime = 1 for vendor in vendors: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) @@ -692,26 +722,26 @@ class SaleOrder(models.Model): if vendor_sla.unit == 'hari': vendor_duration = vendor_sla.duration * 24 * 60 include_instant = False - else : + else: vendor_duration = vendor_sla.duration * 60 include_instant = True - + estimation_sla = (1 * 24 * 60) + vendor_duration estimation_sla_days = estimation_sla / (24 * 60) slatime = math.ceil(estimation_sla_days) - + max_slatime = max(max_slatime, slatime) return {'slatime': max_slatime, 'include_instant': include_instant} - + def _calculate_etrts_date(self): 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']) @@ -719,29 +749,28 @@ class SaleOrder(models.Model): 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 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 + + @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: 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() - + 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( @@ -750,16 +779,16 @@ class SaleOrder(models.Model): ) else: rec.commitment_date = rec.expected_ready_to_ship - - - @api.onchange('expected_ready_to_ship') #Hangle Onchange form 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: if order.state in ('done', 'cancel', 'sale'): - raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) + raise UserError( + _("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) def _prepare_invoice(self): @@ -771,10 +800,12 @@ class SaleOrder(models.Model): self.ensure_one() journal = self.env['account.move'].with_context(default_move_type='out_invoice')._get_default_journal() if not journal: - raise UserError(_('Please define an accounting sales journal for the company %s (%s).') % (self.company_id.name, self.company_id.id)) + raise UserError( + _('Please define an accounting sales journal for the company %s (%s).') % (self.company_id.name, + self.company_id.id)) parent_id = self.partner_id.parent_id - parent_id = parent_id if parent_id else self.partner_id + parent_id = parent_id if parent_id else self.partner_id invoice_vals = { 'ref': self.client_order_ref or '', @@ -791,7 +822,8 @@ class SaleOrder(models.Model): 'partner_id': parent_id.id, 'partner_shipping_id': parent_id.id, 'real_invoice_id': self.real_invoice_id.id, - 'fiscal_position_id': (self.fiscal_position_id or self.fiscal_position_id.get_fiscal_position(self.partner_invoice_id.id)).id, + 'fiscal_position_id': (self.fiscal_position_id or self.fiscal_position_id.get_fiscal_position( + self.partner_invoice_id.id)).id, 'partner_bank_id': self.company_id.partner_id.bank_ids[:1].id, 'journal_id': journal.id, # company comes from the journal 'invoice_origin': self.name, @@ -807,10 +839,10 @@ class SaleOrder(models.Model): def _validate_email(self): rule_regex = self.env['ir.config_parameter'].sudo().get_param('sale.order.validate_email') or '' pattern = rf'^{rule_regex}$' - + 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') def _validate_delivery_amt(self): is_indoteknik = self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik' @@ -822,13 +854,14 @@ class SaleOrder(models.Model): raise UserError('Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum diisi.') else: raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum diisi.') - + if self.delivery_amt < 100: if self.carrier_id.id == 1: - raise UserError('Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum memenuhi tarif minimum.') + raise UserError( + 'Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum memenuhi tarif minimum.') else: - raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum memenuhi tarif minimum.') - + raise UserError( + 'Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum memenuhi tarif minimum.') # if self.delivery_amt < 5000: # if (self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik') and not self.env.context.get('active_id', []): @@ -837,7 +870,6 @@ class SaleOrder(models.Model): # else: # raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum memenuhi jumlah minimum.') - def override_allow_create_invoice(self): if not self.env.user.is_accounting: raise UserError('Hanya Finance Accounting yang dapat klik tombol ini') @@ -878,14 +910,14 @@ class SaleOrder(models.Model): 'sale_ids': [x.id for x in self] } return action - + def open_form_multi_update_state(self): action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_quotation_so_multi_update') action['context'] = { 'quotation_ids': [x.id for x in self] } return action - + def action_multi_update_invoice_status(self): for sale in self: sale.update({ @@ -898,7 +930,7 @@ class SaleOrder(models.Model): for line in order.order_line: total += line.vendor_subtotal order.purchase_total = total - + def check_data_real_delivery_address(self): real_delivery_address = self.real_shipping_id @@ -918,8 +950,8 @@ class SaleOrder(models.Model): def generate_payment_link_midtrans_sales_order(self): # midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox # midtrans_auth = 'Basic U0ItTWlkLXNlcnZlci1uLVY3ZDJjMlpCMFNWRUQyOU95Q1dWWXA6' # dev - sandbox - midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production - midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production + midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production + midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production so_number = self.name so_number = so_number.replace('/', '-') so_grandtotal = math.floor(self.grand_total) @@ -934,7 +966,8 @@ class SaleOrder(models.Model): if check_response.status_code == 200: status_response = check_response.json() - if status_response.get('transaction_status') == 'expire' or status_response.get('transaction_status') == 'cancel': + if status_response.get('transaction_status') == 'expire' or status_response.get( + 'transaction_status') == 'cancel': so_number = so_number + '-cpl' json_data = { @@ -982,8 +1015,7 @@ class SaleOrder(models.Model): if line.product_id.type == 'product': line_no += 1 line.line_no = line_no - - + def write(self, vals): if 'carrier_id' in vals: for picking in self.picking_ids: @@ -996,7 +1028,7 @@ class SaleOrder(models.Model): ('state', 'in', so_state), ('so_status', '!=', 'terproses'), ]) - + for sale in sales: picking_states = ['draft', 'assigned', 'confirmed', 'waiting'] have_outstanding_pick = any(x.state in picking_states for x in sale.picking_ids) @@ -1010,16 +1042,16 @@ class SaleOrder(models.Model): sale.so_status = 'terproses' else: sale.so_status = 'menunggu' - + for picking in sale.picking_ids: sum_qty_pick = sum(move_line.product_uom_qty for move_line in picking.move_ids_without_package) sum_qty_reserved = sum(move_line.product_uom_qty for move_line in picking.move_line_ids_without_package) if picking.state == 'done': continue - elif sum_qty_pick == sum_qty_reserved and not picking.date_reserved:# baru ke reserved + elif sum_qty_pick == sum_qty_reserved and not picking.date_reserved: # baru ke reserved current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') picking.date_reserved = current_time - elif sum_qty_pick == sum_qty_reserved:# sudah ada data reserved + elif sum_qty_pick == sum_qty_reserved: # sudah ada data reserved picking.date_reserved = picking.date_reserved else: picking.date_reserved = '' @@ -1054,7 +1086,7 @@ class SaleOrder(models.Model): @api.onchange('partner_id') def onchange_partner_contact(self): parent_id = self.partner_id.parent_id - parent_id = parent_id if parent_id else self.partner_id + parent_id = parent_id if parent_id else self.partner_id self.npwp = parent_id.npwp self.sppkp = parent_id.sppkp @@ -1062,13 +1094,13 @@ class SaleOrder(models.Model): self.email = parent_id.email self.pareto_status = parent_id.pareto_status self.user_id = parent_id.user_id - + @api.onchange('partner_id') def onchange_partner_id(self): # INHERIT result = super(SaleOrder, self).onchange_partner_id() parent_id = self.partner_id.parent_id - parent_id = parent_id if parent_id else self.partner_id + parent_id = parent_id if parent_id else self.partner_id self.partner_invoice_id = parent_id return result @@ -1101,16 +1133,16 @@ class SaleOrder(models.Model): minimum_amount = 20000000 for order in self: order.have_visit_service = self.amount_total > minimum_amount - + def _get_helper_ids(self): helper_ids_str = self.env['ir.config_parameter'].sudo().get_param('sale.order.user_helper_ids') return helper_ids_str.split(', ') - + def write(self, values): helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: values['helper_by_id'] = self.env.user.id - + return super(SaleOrder, self).write(values) def check_due(self): @@ -1128,21 +1160,21 @@ class SaleOrder(models.Model): def _validate_order(self): if self.payment_term_id.id == 31 and self.total_percent_margin < 25: raise UserError("Jika ingin menggunakan Tempo 90 Hari maka margin harus di atas 25%") - - if self.warehouse_id.id != 8 and self.warehouse_id.id != 10: #GD Bandengan + + if self.warehouse_id.id != 8 and self.warehouse_id.id != 10: # GD Bandengan raise UserError('Gudang harus Bandengan') - + if self.state not in ['draft', 'sent']: raise UserError("Status harus draft atau sent") - + self._validate_npwp() - + def _validate_npwp(self): num_digits = sum(c.isdigit() for c in self.npwp) if num_digits < 10: raise UserError("NPWP harus memiliki minimal 10 digit") - + # pattern = r'^\d{10,}$' # return re.match(pattern, self.npwp) is not None @@ -1170,7 +1202,8 @@ class SaleOrder(models.Model): if (partner.customer_type == 'pkp' or order.customer_type == 'pkp') and order.sppkp != partner.sppkp: raise UserError("SPPKP berbeda pada Master Data Customer") if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): - raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") + 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") @@ -1178,20 +1211,23 @@ class SaleOrder(models.Model): for order in self: for line in order.order_line: 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') + 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.") + raise UserError( + "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") else: raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") - + def check_duplicate_product(self): for order in self: for line in order.order_line: - search_product = self.env['sale.order.line'].search([('product_id', '=', line.product_id.id), ('order_id', '=', order.id)]) - if len(search_product) > 1: - raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name)) + search_product = self.env['sale.order.line'].search( + [('product_id', '=', line.product_id.id), ('order_id', '=', order.id)]) + if len(search_product) > 1: + raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name)) def sale_order_approve(self): self.check_duplicate_product() @@ -1214,11 +1250,13 @@ class SaleOrder(models.Model): SYSTEM_UID = 25 FROM_WEBSITE = order.create_uid.id == SYSTEM_UID - if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval not in ['cust_procurement','cust_director']: + if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval not in ['cust_procurement', + 'cust_director']: raise UserError("This order not yet approved by customer procurement or director") if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): - raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") + raise UserError( + "Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") if not order.commitment_date and order.create_date > datetime(2024, 9, 12): raise UserError("Expected Delivery Date kosong, wajib diisi") @@ -1227,7 +1265,8 @@ class SaleOrder(models.Model): UserError('Real Delivery Address harus di isi') if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification','Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + return self._create_notification_action('Notification', + 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') term_days = 0 for term_line in order.payment_term_id.line_ids: @@ -1245,13 +1284,15 @@ class SaleOrder(models.Model): if (partner.customer_type == 'pkp' or order.customer_type == 'pkp') and order.sppkp != partner.sppkp: raise UserError("SPPKP berbeda pada Master Data Customer") if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): - raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") + 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") - + if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - + return self._create_notification_action('Notification', + 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' return self._create_approval_notification('Pimpinan') @@ -1261,9 +1302,9 @@ class SaleOrder(models.Model): self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') - + raise UserError("Bisa langsung Confirm") - + def send_notif_to_salesperson(self, cancel=False): if not cancel: @@ -1281,7 +1322,9 @@ class SaleOrder(models.Model): salesperson_data = {} for rec in grouping_so: if rec.user_id.id not in salesperson_data: - salesperson_data[rec.user_id.id] = {'name': rec.user_id.name, 'orders': [], 'total_amount': 0, 'sum_total_amount': 0, 'business_partner': '', 'site': ''} # Menetapkan nilai awal untuk 'site' + salesperson_data[rec.user_id.id] = {'name': rec.user_id.name, 'orders': [], 'total_amount': 0, + 'sum_total_amount': 0, 'business_partner': '', + 'site': ''} # Menetapkan nilai awal untuk 'site' if rec.picking_ids: if not any(picking.state in ['assigned', 'confirmed', 'waiting'] for picking in rec.picking_ids): continue @@ -1300,7 +1343,8 @@ class SaleOrder(models.Model): }) salesperson_data[rec.user_id.id]['sum_total_amount'] += order_total_amount salesperson_data[rec.user_id.id]['business_partner'] = grouping_so[0].partner_id.main_parent_id.name - salesperson_data[rec.user_id.id]['site'] = grouping_so[0].partner_id.site_id.name # Menambahkan nilai hanya jika ada + salesperson_data[rec.user_id.id]['site'] = grouping_so[ + 0].partner_id.site_id.name # Menambahkan nilai hanya jika ada # Kirim email untuk setiap salesperson for salesperson_id, data in salesperson_data.items(): @@ -1324,9 +1368,9 @@ class SaleOrder(models.Model): template = self.env.ref('indoteknik_custom.mail_template_sale_order_notification_to_salesperson') email_body = template.body_html.replace('${table_content}', table_content) email_body = email_body.replace('${salesperson_name}', data['name']) - email_body = email_body.replace('${sum_total_amount}', str(data['sum_total_amount'])) - email_body = email_body.replace('${site}', str(data['site'])) - email_body = email_body.replace('${business_partner}', str(data['business_partner'])) + email_body = email_body.replace('${sum_total_amount}', str(data['sum_total_amount'])) + email_body = email_body.replace('${site}', str(data['site'])) + email_body = email_body.replace('${business_partner}', str(data['business_partner'])) # Kirim email self.env['mail.mail'].create({ 'subject': 'Notification: Sale Orders', @@ -1362,8 +1406,9 @@ class SaleOrder(models.Model): if (outstanding_amount + rec.amount_total) >= block_stage: if block_stage != 0: remaining_credit_limit = block_stage - outstanding_amount - raise UserError(_("%s is in Blocking Stage, Remaining credit limit is %s, from %s and outstanding %s") - % (rec.partner_id.name, remaining_credit_limit, block_stage, outstanding_amount)) + raise UserError( + _("%s is in Blocking Stage, Remaining credit limit is %s, from %s and outstanding %s") + % (rec.partner_id.name, remaining_credit_limit, block_stage, outstanding_amount)) def check_limit_so_to_invoice(self): for rec in self: @@ -1385,7 +1430,8 @@ class SaleOrder(models.Model): # Validasi limit if remaining_credit_limit <= 0 and block_stage > 0 and not is_cbd: - raise UserError(_("The credit limit for %s will exceed the Blocking Stage if the Sale Order is confirmed. The remaining credit limit is %s, from %s and the outstanding amount is %s.") + raise UserError( + _("The credit limit for %s will exceed the Blocking Stage if the Sale Order is confirmed. The remaining credit limit is %s, from %s and the outstanding amount is %s.") % (rec.partner_id.name, block_stage - current_total, block_stage, outstanding_amount)) def validate_different_vendor(self): @@ -1395,11 +1441,11 @@ class SaleOrder(models.Model): if self.vendor_approval_id and all(v.state != 'draft' for v in self.vendor_approval_id): return False - + different_vendor = self.order_line.filtered( lambda l: l.vendor_id and l.vendor_md_id and l.vendor_id.id != l.vendor_md_id.id ) - + if different_vendor: vendor_approvals = [] for line in different_vendor: @@ -1425,15 +1471,14 @@ class SaleOrder(models.Model): if line.purchase_price_md else False ), }) - + vendor_approvals.append(vendor_approval.id) - + self.vendor_approval_id = [(4, vid) for vid in vendor_approvals] return True else: return False - def action_confirm(self): for order in self: order._validate_uniform_taxes() @@ -1443,38 +1488,41 @@ class SaleOrder(models.Model): order.check_limit_so_to_invoice() if self.validate_different_vendor() and not self.vendor_approval: return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor') - + order.check_data_real_delivery_address() order.sale_order_check_approve() order._validate_order() order.order_line.validate_line() - + main_parent = order.partner_id.get_main_parent() SYSTEM_UID = 25 FROM_WEBSITE = order.create_uid.id == SYSTEM_UID - - if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval not in ['cust_procurement', 'cust_director']: + + if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval not in ['cust_procurement', + 'cust_director']: raise UserError("This order not yet approved by customer procurement or director") if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): - raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") + raise UserError( + "Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") if not order.commitment_date and order.create_date > datetime(2024, 9, 12): raise UserError("Expected Delivery Date kosong, wajib diisi") - + if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - + return self._create_notification_action('Notification', + 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' return self._create_approval_notification('Pimpinan') elif order._requires_approval_margin_manager(): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') - + order.approval_status = 'approved' order._set_sppkp_npwp_contact() order.calculate_line_no() @@ -1488,7 +1536,7 @@ class SaleOrder(models.Model): for line in order.order_line: if line.display_type == 'line_note': note.append(line.name) - + if order.picking_ids: # Sort picking_ids by creation date to get the most recent one latest_picking = order.picking_ids.sorted(key=lambda p: p.create_date, reverse=True)[0] @@ -1502,7 +1550,7 @@ class SaleOrder(models.Model): main_parent = self.partner_id.get_main_parent() if self._name != 'sale.order': return super(SaleOrder, self).action_cancel() - + if self.have_outstanding_invoice: raise UserError("Invoice harus di Cancel dahulu") @@ -1513,7 +1561,7 @@ class SaleOrder(models.Model): for line in self.order_line: if line.qty_delivered > 0: raise UserError("DO yang done harus di-Return oleh Logistik") - + if not self.web_approval: self.web_approval = 'company' # elif self.have_outstanding_po: @@ -1543,11 +1591,11 @@ class SaleOrder(models.Model): def validate_partner_invoice_due(self): parent_id = self.partner_id.parent_id.id - parent_id = parent_id if parent_id else self.partner_id.id + parent_id = parent_id if parent_id else self.partner_id.id if self.due_id and self.due_id.is_approve == False: raise UserError('Document Over Due Yang Anda Buat Belum Di Approve') - + query = [ ('partner_id', '=', parent_id), ('state', '=', 'posted'), @@ -1570,27 +1618,27 @@ class SaleOrder(models.Model): else: due_extension.unlink() return False - + def _requires_approval_margin_leader(self): return self.total_percent_margin <= 15 and not self.env.user.is_leader - + def _requires_approval_margin_manager(self): return 15 < self.total_percent_margin <= 24 and not self.env.user.is_sales_manager and not self.env.user.is_leader # return self.total_percent_margin >= 15 and not self.env.user.is_leader and not self.env.user.is_sales_manager - + def _create_approval_notification(self, approval_role): title = 'Warning' message = f'SO butuh approval {approval_role}' return self._create_notification_action(title, message) - + def _create_notification_action(self, title, message): return { 'type': 'ir.actions.client', 'tag': 'display_notification', - 'params': { 'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'} }, + 'params': {'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'}}, } - - def _set_sppkp_npwp_contact(self): + + def _set_sppkp_npwp_contact(self): partner = self.partner_id.parent_id or self.partner_id if not partner.sppkp: @@ -1609,7 +1657,7 @@ class SaleOrder(models.Model): # partner.npwp = self.npwp # partner.sppkp = self.sppkp # partner.email = self.email - + def _compute_total_margin(self): for order in self: total_margin = sum(line.item_margin for line in order.order_line if line.product_id) @@ -1617,7 +1665,7 @@ class SaleOrder(models.Model): total_margin -= order.ongkir_ke_xpdc order.total_margin = total_margin - + def _compute_total_before_margin(self): for order in self: total_before_margin = sum(line.item_before_margin for line in order.order_line if line.product_id) @@ -1632,9 +1680,10 @@ class SaleOrder(models.Model): delivery_amt = order.delivery_amt else: delivery_amt = 0 - + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) - order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-order.fee_third_party-order.biaya_lain_lain)) * 100, 2) + order.total_percent_margin = round( + (order.total_margin / (order.amount_untaxed - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) @api.onchange('sales_tax_id') @@ -1657,20 +1706,20 @@ class SaleOrder(models.Model): voucher = self.voucher_id if voucher.limit > 0 and voucher.count_order >= voucher.limit: raise UserError('Voucher tidak dapat digunakan karena sudah habis digunakan') - + partner_voucher_orders = [] for order in voucher.order_ids: if order.partner_id.id == self.partner_id.id: partner_voucher_orders.append(order) - + if voucher.limit_user > 0 and len(partner_voucher_orders) >= voucher.limit_user: raise UserError('Voucher tidak dapat digunakan karena Customer ini sudah menghabiskan kuota voucher') - + if self.pricelist_id.id in [x.id for x in voucher.excl_pricelist_ids]: raise UserError('Voucher tidak dapat digunakan karena pricelist ini tidak berlaku pada voucher') - + self.apply_voucher() - + def action_apply_voucher_shipping(self): for line in self.order_line: if line.order_promotion_id: @@ -1679,18 +1728,18 @@ class SaleOrder(models.Model): voucher = self.voucher_shipping_id if voucher.limit > 0 and voucher.count_order >= voucher.limit: raise UserError('Voucher tidak dapat digunakan karena sudah habis digunakan') - + partner_voucher_orders = [] for order in voucher.order_ids: if order.partner_id.id == self.partner_id.id: partner_voucher_orders.append(order) - + if voucher.limit_user > 0 and len(partner_voucher_orders) >= voucher.limit_user: raise UserError('Voucher tidak dapat digunakan karena Customer ini sudah menghabiskan kuota voucher') - + if self.pricelist_id.id in [x.id for x in voucher.excl_pricelist_ids]: raise UserError('Voucher tidak dapat digunakan karena pricelist ini tidak berlaku pada voucher') - + self.apply_voucher_shipping() def apply_voucher(self): @@ -1707,7 +1756,7 @@ class SaleOrder(models.Model): for line in self.order_line: line.initial_discount = line.discount - + voucher_type = voucher['type'] used_total = voucher['total'][voucher_type] used_discount = voucher['discount'][voucher_type] @@ -1723,11 +1772,11 @@ class SaleOrder(models.Model): line_contribution = line.price_subtotal / used_total line_voucher = used_discount * line_contribution line_voucher_item = line_voucher / line.product_uom_qty - + line_price_unit = line.price_unit / 1.11 if any(tax.id == 23 for tax in line.tax_id) else line.price_unit line_discount_item = line_price_unit * line.discount / 100 + line_voucher_item line_voucher_item = line_discount_item / line_price_unit * 100 - + line.amount_voucher_disc = line_voucher line.discount = line_voucher_item @@ -1738,27 +1787,27 @@ class SaleOrder(models.Model): for order in self: delivery_amt = order.delivery_amt voucher = order.voucher_shipping_id - + if voucher: max_discount_amount = voucher.discount_amount voucher_type = voucher.discount_type - + if voucher_type == 'fixed_price': discount = max_discount_amount elif voucher_type == 'percentage': discount = delivery_amt * (max_discount_amount / 100) - + delivery_amt -= discount - + delivery_amt = max(delivery_amt, 0) - + order.delivery_amt = delivery_amt - + order.amount_voucher_shipping_disc = discount order.applied_voucher_shipping_id = order.voucher_id.id def cancel_voucher(self): - self.applied_voucher_id = False + self.applied_voucher_id = False self.amount_voucher_disc = 0 for line in self.order_line: line.amount_voucher_disc = 0 @@ -1767,17 +1816,18 @@ class SaleOrder(models.Model): def cancel_voucher_shipping(self): self.delivery_amt + self.amount_voucher_shipping_disc - self.applied_voucher_shipping_id = False + self.applied_voucher_shipping_id = False self.amount_voucher_shipping_disc = 0 def action_web_approve(self): if self.env.uid != self.partner_id.user_id.id: - raise UserError('You are not authorized to approve this order. Only %s can approve this order.' % self.partner_id.user_id.name) - + raise UserError( + 'You are not authorized to approve this order. Only %s can approve this order.' % self.partner_id.user_id.name) + self.web_approval = 'company' template = self.env.ref('indoteknik_custom.mail_template_sale_order_web_approve_notification') template.send_mail(self.id, force_send=True) - + return { 'type': 'ir.actions.client', 'tag': 'display_notification', @@ -1837,13 +1887,14 @@ class SaleOrder(models.Model): if last_so and rec_purchase_price != last_so.purchase_price: rec_taxes = self.env['account.tax'].search([('id', '=', rec_taxes_id)], limit=1) if rec_taxes.price_include: - selling_price = (rec_purchase_price / 1.11) / (1 - (last_so.item_percent_margin_without_deduction / 100)) + selling_price = (rec_purchase_price / 1.11) / ( + 1 - (last_so.item_percent_margin_without_deduction / 100)) else: selling_price = rec_purchase_price / (1 - (last_so.item_percent_margin_without_deduction / 100)) tax_id = last_so.tax_id for tax in tax_id: if tax.price_include: - selling_price = selling_price + (selling_price*11/100) + selling_price = selling_price + (selling_price * 11 / 100) else: selling_price = selling_price discount = 0 @@ -1859,13 +1910,14 @@ class SaleOrder(models.Model): elif last_so and rec_vendor_id == order_line.vendor_id.id and rec_purchase_price != last_so.purchase_price: rec_taxes = self.env['account.tax'].search([('id', '=', rec_taxes_id)], limit=1) if rec_taxes.price_include: - selling_price = (rec_purchase_price / 1.11) / (1 - (last_so.item_percent_margin_without_deduction / 100)) + selling_price = (rec_purchase_price / 1.11) / ( + 1 - (last_so.item_percent_margin_without_deduction / 100)) else: selling_price = rec_purchase_price / (1 - (last_so.item_percent_margin_without_deduction / 100)) tax_id = last_so.tax_id for tax in tax_id: if tax.price_include: - selling_price = selling_price + (selling_price*11/100) + selling_price = selling_price + (selling_price * 11 / 100) else: selling_price = selling_price discount = 0 @@ -1896,14 +1948,14 @@ class SaleOrder(models.Model): return order # def write(self, vals): - # Call the super method to handle the write operation - # res = super(SaleOrder, self).write(vals) - # self._compute_etrts_date() - # Check if the update is coming from a save operation - # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): - # self._update_partner_details() + # Call the super method to handle the write operation + # res = super(SaleOrder, self).write(vals) + # self._compute_etrts_date() + # Check if the update is coming from a save operation + # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): + # self._update_partner_details() - # return res + # return res def _update_partner_details(self): for order in self: @@ -1932,11 +1984,11 @@ 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) # self._check_total_margin_excl_third_party() if any(fields in vals for fields in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): self._validate_delivery_amt() if any(field in vals for field in ["order_line", "client_order_ref"]): - self._calculate_etrts_date() - return res \ No newline at end of file + self._calculate_etrts_date() + return res -- cgit v1.2.3 From 509a771b8ba6122cc308323dbe90ff4a35e4fc72 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 10:21:06 +0700 Subject: add multiparts product to barcoding product --- indoteknik_custom/models/barcoding_product.py | 34 +++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/barcoding_product.py b/indoteknik_custom/models/barcoding_product.py index 204c6128..5f4e80ea 100644 --- a/indoteknik_custom/models/barcoding_product.py +++ b/indoteknik_custom/models/barcoding_product.py @@ -12,7 +12,7 @@ class BarcodingProduct(models.Model): barcoding_product_line = fields.One2many('barcoding.product.line', 'barcoding_product_id', string='Barcoding Product Lines', auto_join=True) product_id = fields.Many2one('product.product', string="Product", tracking=3) quantity = fields.Float(string="Quantity", tracking=3) - type = fields.Selection([('print', 'Print Barcode'), ('barcoding', 'Add Barcode To Product'), ('barcoding_box', 'Add Barcode Box To Product')], string='Type', default='print') + type = fields.Selection([('print', 'Print Barcode'), ('barcoding', 'Add Barcode To Product'), ('barcoding_box', 'Add Barcode Box To Product'), ('multiparts', 'Multiparts Product')], string='Type', default='print') barcode = fields.Char(string="Barcode") qty_pcs_box = fields.Char(string="Quantity Pcs Box") @@ -40,16 +40,33 @@ class BarcodingProduct(models.Model): @api.onchange('product_id', 'quantity') def _onchange_product_or_quantity(self): - """Update barcoding_product_line based on product_id and quantity""" if self.product_id and self.quantity > 0: # Clear existing lines self.barcoding_product_line = [(5, 0, 0)] # Add a new line with the current product and quantity - self.barcoding_product_line = [(0, 0, { - 'product_id': self.product_id.id, - 'barcoding_product_id': self.id, - }) for _ in range(int(self.quantity))] + lines = [] + for i in range(int(self.quantity)): + lines.append((0, 0, { + 'product_id': self.product_id.id, + 'barcoding_product_id': self.id, + 'sequence_with_total': f"{i+1}/{int(self.quantity)}" + })) + self.barcoding_product_line = lines + + def write(self, vals): + """Override write to update sequence_with_total when quantity changes""" + res = super().write(vals) + if 'quantity' in vals and self.type == 'multiparts': + self._update_sequence_with_total() + return res + + def _update_sequence_with_total(self): + """Update sequence_with_total for all lines""" + for rec in self: + total = int(rec.quantity) + for index, line in enumerate(rec.barcoding_product_line, start=1): + line.sequence_with_total = f"{index}/{total}" class BarcodingProductLine(models.Model): @@ -59,4 +76,7 @@ class BarcodingProductLine(models.Model): barcoding_product_id = fields.Many2one('barcoding.product', string='Barcoding Product Ref', required=True, ondelete='cascade', index=True, copy=False) product_id = fields.Many2one('product.product', string="Product") - qr_code_variant = fields.Binary("QR Code Variant", related='product_id.qr_code_variant') \ No newline at end of file + qr_code_variant = fields.Binary("QR Code Variant", related='product_id.qr_code_variant') + sequence_with_total = fields.Char( + string="Sequence" + ) \ No newline at end of file -- cgit v1.2.3 From c5506e8e7c22b02968d4a8ae56b7147ee85e686a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 11:24:04 +0700 Subject: fix bug --- indoteknik_custom/models/barcoding_product.py | 4 ---- indoteknik_custom/models/product_template.py | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/barcoding_product.py b/indoteknik_custom/models/barcoding_product.py index 5f4e80ea..335b481a 100644 --- a/indoteknik_custom/models/barcoding_product.py +++ b/indoteknik_custom/models/barcoding_product.py @@ -41,10 +41,8 @@ class BarcodingProduct(models.Model): @api.onchange('product_id', 'quantity') def _onchange_product_or_quantity(self): if self.product_id and self.quantity > 0: - # Clear existing lines self.barcoding_product_line = [(5, 0, 0)] - # Add a new line with the current product and quantity lines = [] for i in range(int(self.quantity)): lines.append((0, 0, { @@ -55,14 +53,12 @@ class BarcodingProduct(models.Model): self.barcoding_product_line = lines def write(self, vals): - """Override write to update sequence_with_total when quantity changes""" res = super().write(vals) if 'quantity' in vals and self.type == 'multiparts': self._update_sequence_with_total() return res def _update_sequence_with_total(self): - """Update sequence_with_total for all lines""" for rec in self: total = int(rec.quantity) for index, line in enumerate(rec.barcoding_product_line, start=1): diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 2679fbfd..03c7ced4 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -1320,10 +1320,11 @@ class ProductProduct(models.Model): # 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_product_variants(vals, old_values) - return result + if self.default_code in vals: + old_values = self._collect_old_values(vals) + result = super().write(vals) + self._log_field_changes_product_variants(vals, old_values) + return result class OutstandingMove(models.Model): _name = 'v.move.outstanding' -- cgit v1.2.3 From 2217e86706fcaf03da2c05761b4f837e90374ec2 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 11:37:01 +0700 Subject: fix bug --- indoteknik_custom/models/product_template.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 03c7ced4..17805c6c 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -1320,11 +1320,19 @@ class ProductProduct(models.Model): # simpan data lama dan log perubahan field def write(self, vals): - if self.default_code in vals: + tracked_fields = [ + 'default_code', 'name', 'weight', 'x_manufacture', + 'public_categ_ids', 'search_rank', 'search_rank_weekly', + 'image_1920', 'unpublished', 'image_carousel_lines' + ] + + if any(field in vals for field in tracked_fields): old_values = self._collect_old_values(vals) result = super().write(vals) self._log_field_changes_product_variants(vals, old_values) return result + else: + return super().write(vals) class OutstandingMove(models.Model): _name = 'v.move.outstanding' -- cgit v1.2.3 From 1d7a2902b1b97e6fb7951252cf851695d6a8ee8e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 15:09:46 +0700 Subject: shipment group --- .../models/account_move_due_extension.py | 2 +- indoteknik_custom/models/sale_order.py | 47 +++++++++++----------- indoteknik_custom/models/shipment_group.py | 3 ++ indoteknik_custom/models/stock_picking.py | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index c48c2372..4a3f40e2 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -96,7 +96,7 @@ class DueExtension(models.Model): sales = self.env['sale.order'].browse(self.order_id.id) - sales.action_confirm() + sales.with_context({'due_approve': True}).action_confirm() self.order_id.due_id = self.id self.approve_by = self.env.user.id self.date_approve = datetime.utcnow() diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 99aa8053..8aff6b3a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1264,9 +1264,10 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', - 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + if self.env.context.get('due_approve', []) == False: + if order.validate_partner_invoice_due(): + return self._create_notification_action('Notification', + 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') term_days = 0 for term_line in order.payment_term_id.line_ids: @@ -1289,9 +1290,9 @@ class SaleOrder(models.Model): if not order.user_id.active: raise UserError("Salesperson sudah tidak aktif, mohon diisi yang benar pada data SO dan Contact") - if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', - 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + # if order.validate_partner_invoice_due(): + # return self._create_notification_action('Notification', + # 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' @@ -1511,10 +1512,11 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - - if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', - 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + + if self.env.context.get('due_approve', []) == False: + if order.validate_partner_invoice_due(): + return self._create_notification_action('Notification', + 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' @@ -1605,19 +1607,18 @@ class SaleOrder(models.Model): invoices = self.env['account.move'].search(query, order='invoice_date') if invoices: - if not self.env.user.is_leader and not self.env.user.is_sales_manager: - due_extension = self.env['due.extension'].create([{ - 'partner_id': parent_id, - 'day_extension': '3', - 'order_id': self.id, - }]) - due_extension.generate_due_line() - self.due_id = due_extension.id - if len(self.due_id.due_line) > 0: - return True - else: - due_extension.unlink() - return False + due_extension = self.env['due.extension'].create([{ + 'partner_id': parent_id, + 'day_extension': '3', + 'order_id': self.id, + }]) + due_extension.generate_due_line() + self.due_id = due_extension.id + if len(self.due_id.due_line) > 0: + return True + else: + due_extension.unlink() + return False def _requires_approval_margin_leader(self): return self.total_percent_margin <= 15 and not self.env.user.is_leader diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py index b7d7ac12..a4bea9c4 100644 --- a/indoteknik_custom/models/shipment_group.py +++ b/indoteknik_custom/models/shipment_group.py @@ -93,6 +93,9 @@ class ShipmentGroupLine(models.Model): if self.shipment_id.carrier_id and self.shipment_id.carrier_id != picking.carrier_id: raise UserError('carrier must be same as shipment group') + if picking.total_mapping_koli == 0: + raise UserError(f'Picking {picking.name} tidak memiliki mapping koli') + self.partner_id = picking.partner_id self.shipping_paid_by = picking.sale_id.shipping_paid_by self.carrier_id = picking.carrier_id.id diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6a6fe352..ac812a32 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -280,7 +280,7 @@ class StockPicking(models.Model): state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') - last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim') + last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim', copy=False) update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD') def _get_kgx_awb_number(self): -- cgit v1.2.3 From 14b8ea0d6a3c091d63c10768fe61a02eeb55d49d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 16:59:54 +0700 Subject: unlock validasi --- indoteknik_custom/models/sale_order.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8aff6b3a..366fcc6a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1213,13 +1213,13 @@ class SaleOrder(models.Model): 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') - if not confirmed_bom: - raise UserError( - "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") - else: - raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") + # 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: + # raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") def check_duplicate_product(self): for order in self: @@ -1512,7 +1512,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if self.env.context.get('due_approve', []) == False: if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', -- cgit v1.2.3 From 41a69ecfe8513a608c5a0accfb253ba6fb06f60a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 17:01:18 +0700 Subject: lock validasi bu mo --- indoteknik_custom/models/sale_order.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 366fcc6a..8aff6b3a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1213,13 +1213,13 @@ class SaleOrder(models.Model): 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') - # if not confirmed_bom: - # raise UserError( - # "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") - # else: - # raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") + 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: + raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") def check_duplicate_product(self): for order in self: @@ -1512,7 +1512,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if self.env.context.get('due_approve', []) == False: if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', -- cgit v1.2.3 From d70d65c19dccb2cc4264385c121c697ecd3fa8cd Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 17:03:55 +0700 Subject: add validation state so on mrp production --- indoteknik_custom/models/mrp_production.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 8179fe56..58c2512c 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -244,6 +244,10 @@ class CheckBomProduct(models.Model): @api.constrains('production_id', 'product_id') def _check_product_bom_validation(self): for record in self: + if record.production_id.sale_order.state not in ['sale', 'done']: + raise UserError(( + "SO harus diconfirm terlebih dahulu." + )) if not record.production_id or not record.product_id: continue -- cgit v1.2.3 From 00cc0e587ad443fc5b3003ce80fdc1eef96ebb7b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 14 May 2025 17:09:32 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8aff6b3a..6550b859 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1624,7 +1624,7 @@ class SaleOrder(models.Model): return self.total_percent_margin <= 15 and not self.env.user.is_leader def _requires_approval_margin_manager(self): - return 15 < self.total_percent_margin <= 24 and not self.env.user.is_sales_manager and not self.env.user.is_leader + return 15 < self.total_percent_margin <= 24 and not self.env.user.is_sales_manager and not self.env.user.id == 375 and not self.env.user.is_leader # return self.total_percent_margin >= 15 and not self.env.user.is_leader and not self.env.user.is_sales_manager def _create_approval_notification(self, approval_role): -- cgit v1.2.3 From c40dc2dafb93759c9fdcda8581eb0c402d304886 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 09:13:00 +0700 Subject: fix date approval invoice date --- indoteknik_custom/models/approval_invoice_date.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/approval_invoice_date.py b/indoteknik_custom/models/approval_invoice_date.py index 48546e55..b58d55d4 100644 --- a/indoteknik_custom/models/approval_invoice_date.py +++ b/indoteknik_custom/models/approval_invoice_date.py @@ -31,7 +31,7 @@ class ApprovalInvoiceDate(models.Model): def button_approve(self): if not self.env.user.is_accounting: raise UserError("Hanya Accounting Yang Bisa Approve") - self.move_id.invoice_date = self.date_doc_do + self.move_id.invoice_date = self.date_doc_do.date() self.state = 'done' self.approve_date = datetime.utcnow() self.approve_by = self.env.user.id -- cgit v1.2.3 From 562f34bf5eb4b216ddf845813e405704f3842789 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 11:39:09 +0700 Subject: disable reserve --- indoteknik_custom/models/stock_picking.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ac812a32..1c4a6cbc 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1012,6 +1012,8 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): + if self.sale_id.hold_outgoing == True and self.location_id == 57 and self.location_dest_id == 60: + raise UserError("SO on hold") res = super(StockPicking, self).action_assign() current_time = datetime.datetime.utcnow() self.real_shipping_id = self.sale_id.real_shipping_id -- cgit v1.2.3 From aac9e231854f3f7ad112fc57ef53d2edac662c7a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 11:43:42 +0700 Subject: disable reserve --- indoteknik_custom/models/sale_order.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6550b859..1242108e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -372,6 +372,9 @@ class SaleOrder(models.Model): if self.hold_outgoing == True: self.hold_outgoing = False else: + pick = self.env['stock.picking'].search([('sale_id', '=', self.id), ('state', 'not in', ['cancel','done']),('BU/PICK/', 'in', self.name)]) + for picking in pick: + picking.do_unreserve() self.hold_outgoing = True def _validate_uniform_taxes(self): -- cgit v1.2.3 From 3c170c77e4913313ca28169172dbad8c8726ad5c Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 15 May 2025 13:19:22 +0700 Subject: (andri) add button sync price to SO & penyesuaian readonly pada orderline PO --- indoteknik_custom/models/purchase_order.py | 228 ++++++++++++++++++++++++ indoteknik_custom/models/purchase_order_line.py | 54 ++++++ 2 files changed, 282 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 98b367d0..d5c08660 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -2,6 +2,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta +from odoo.tools import float_compare import logging from pytz import timezone, utc import io @@ -89,6 +90,233 @@ class PurchaseOrder(models.Model): store_name = fields.Char(string='Nama Toko') purchase_order_count = fields.Integer('Purchase Order Count', related='partner_id.purchase_order_count') + + + def sync_price_to_so(self): + updated_lines = [] + skipped_lines = [] # Untuk melacak line yang dilewati karena harga sudah sama + affected_so_ids = set() # Untuk melacak SO mana saja yang terkena dampak + + for order in self: + # Filter hanya line-line yang ditandai + marked_lines = order.order_line.filtered(lambda l: l.mark_po_line) + + if not marked_lines: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Info'), + 'message': _('Tidak ada item yang ditandai untuk disinkronkan'), + 'sticky': False, + 'type': 'info', + } + } + + # Cek apakah ada referensi ke sale order + if not order.sale_order_id and not order.order_sales_match_line: + raise UserError(_("Tidak ada Sales Order yang terkait dengan Purchase Order ini!")) + + # Jika PO dibuat dari SO langsung + if order.sale_order_id: + affected_so_ids.add(order.sale_order_id.id) + for po_line in marked_lines: + # Cari SO line yang terkait + so_line = po_line.so_line_id + if not so_line and po_line.product_id: + # Coba cari berdasarkan product jika tidak ada referensi langsung + so_line = self.env['sale.order.line'].search([ + ('product_id', '=', po_line.product_id.id), + ('order_id', '=', order.sale_order_id.id) + ], limit=1) + + if so_line: + old_price = so_line.purchase_price + + # Cek apakah harga sudah sama + if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0: + skipped_lines.append({ + 'product': po_line.product_id.display_name, + 'so_name': so_line.order_id.name, + 'so_id': so_line.order_id.id, + 'price': old_price, + 'currency': order.currency_id or so_line.order_id.currency_id + }) + else: + so_line.purchase_price = po_line.price_unit + updated_lines.append({ + 'product': po_line.product_id.display_name, + 'so_line_id': so_line.id, + 'so_name': so_line.order_id.name, + 'so_id': so_line.order_id.id, + 'old_price': old_price, + 'new_price': po_line.price_unit, + 'currency': order.currency_id or so_line.order_id.currency_id + }) + + # Hapus tanda setelah sinkronisasi + po_line.mark_po_line = False + + # Jika PO terkait dengan beberapa SO (melalui order_sales_match_line) + elif order.order_sales_match_line: + for po_line in marked_lines: + # Cari match lines yang sesuai dengan product di PO line + match_lines = order.order_sales_match_line.filtered( + lambda m: m.product_id.id == po_line.product_id.id + ) + + for match_line in match_lines: + if match_line.sale_id: + affected_so_ids.add(match_line.sale_id.id) + + so_line = match_line.sale_line_id + if so_line: + old_price = so_line.purchase_price + + # Cek apakah harga sudah sama + if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0: + skipped_lines.append({ + 'product': po_line.product_id.display_name, + 'so_name': so_line.order_id.name, + 'so_id': so_line.order_id.id, + 'price': old_price, + 'currency': order.currency_id or so_line.order_id.currency_id + }) + else: + so_line.purchase_price = po_line.price_unit + updated_lines.append({ + 'product': po_line.product_id.display_name, + 'so_line_id': so_line.id, + 'so_name': so_line.order_id.name, + 'so_id': so_line.order_id.id, + 'old_price': old_price, + 'new_price': po_line.price_unit, + 'currency': order.currency_id or so_line.order_id.currency_id + }) + + # Hapus tanda setelah sinkronisasi + po_line.mark_po_line = False + + # Ambil data SO yang terkena dampak + affected_sales_orders = self.env['sale.order'].browse(list(affected_so_ids)) + + # Buat pesan untuk log + message_body = "" + + # Jika ada line yang diupdate + if updated_lines: + # Kelompokkan perubahan berdasarkan SO + changes_by_so = {} + for line in updated_lines: + so_id = line['so_id'] + if so_id not in changes_by_so: + changes_by_so[so_id] = [] + changes_by_so[so_id].append(line) + + # Buat pesan untuk line yang diupdate + message_body += f"

    Harga Purchase pada Sales Order telah diperbarui dari {self.name}:

    " + + for so_id, lines in changes_by_so.items(): + so = self.env['sale.order'].browse(so_id) + # Buat link ke SO yang bisa diklik + message_body += f"

    Sales Order: {so.name}

      " + for line in lines: + # Format harga dalam format mata uang + currency = line['currency'] + old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency}) + new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency}) + + message_body += f"
    • {line['product']}:
      {old_price_formatted} → {new_price_formatted}
    • " + message_body += "
    " + + # Jika ada line yang dilewati karena harga sudah sama + if skipped_lines: + # Kelompokkan berdasarkan SO + skipped_by_so = {} + for line in skipped_lines: + so_id = line['so_id'] + if so_id not in skipped_by_so: + skipped_by_so[so_id] = [] + skipped_by_so[so_id].append(line) + + # Tambahkan pesan untuk line yang dilewati + if message_body: + message_body += "

    Item berikut dilewati karena harga sudah sama:

    " + else: + message_body += f"

    Tidak ada perubahan harga untuk PO {self.name}:

    " + message_body += "

    Item berikut sudah memiliki harga yang sama di SO:

    " + + for so_id, lines in skipped_by_so.items(): + so = self.env['sale.order'].browse(so_id) + message_body += f"

    Sales Order: {so.name}

      " + for line in lines: + # Format harga dalam format mata uang + currency = line['currency'] + price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['price'], {'display_currency': currency}) + + message_body += f"
    • {line['product']}: {price_formatted}
    • " + message_body += "
    " + + # Posting log message jika ada isi + if message_body: + subject = "Price Sync to SO" if updated_lines else "Price Sync - Harga Sama" + self.message_post(body=message_body, subject=subject) + + # Update juga log di setiap SO yang terkena dampak + if updated_lines: + for so in affected_sales_orders: + so_lines = [line for line in updated_lines if line['so_id'] == so.id] + if so_lines: + # Buat link ke PO yang bisa diklik + so_message = f"

    Harga Purchase diperbarui dari {self.name}:

      " + for line in so_lines: + # Format harga dalam format mata uang + currency = line['currency'] + old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency}) + new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency}) + + so_message += f"
    • {line['product']}:
      {old_price_formatted} → {new_price_formatted}
    • " + so_message += "
    " + so.message_post(body=so_message, subject=f"Price Updated from PO {self.name}") + + # Recalculate margins + if updated_lines and hasattr(self, 'compute_total_margin'): + self.compute_total_margin() + + # Recalculate margins di SO juga + if updated_lines: + for so in affected_sales_orders: + if hasattr(so, 'compute_total_margin'): + so.compute_total_margin() + + # Tentukan pesan notifikasi dan tipe + if updated_lines and skipped_lines: + message = f"{len(updated_lines)} item diperbarui dan {len(skipped_lines)} item dilewati karena harga sudah sama" + title = "Sukses!" + notification_type = "success" + elif updated_lines: + message = f"{len(updated_lines)} item telah diperbarui harganya di {len(affected_so_ids)} Sales Order" + title = "Sukses!" + notification_type = "success" + elif skipped_lines: + message = f"item tersebut ({len(skipped_lines)}) sudah memiliki harga yang sama" + title = "Info" + notification_type = "info" + else: + message = "Tidak ada line yang berhasil diperbarui" + title = "Info" + notification_type = "info" + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _(title), + 'message': _(message), + 'sticky': False, + 'type': notification_type, + } + } # cek payment term def _check_payment_term(self): _logger.info("Check Payment Term Terpanggil") diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 033469b8..766e304b 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -51,6 +51,8 @@ class PurchaseOrderLine(models.Model): contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt') ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt') + mark_po_line = fields.Boolean(string=' ', default=False, help='centang jika anda ingin menandai PO line ini') + def _compute_doc_delivery_amt(self): for line in self: # Inisialisasi nilai default untuk field computed @@ -385,3 +387,55 @@ class PurchaseOrderLine(models.Model): line.delivery_amt_line = delivery_amt * contribution else: line.delivery_amt_line = 0 + + # @api.model + # def create(self, vals): + # """ + # Override method create untuk mencegah penambahan line baru + # jika status PO sudah bukan draft + # """ + # # Cek apakah ada order_id di vals + # if vals.get('order_id'): + # order = self.env['purchase.order'].browse(vals['order_id']) + # # Jika status PO bukan draft, tolak pembuatan line baru + # if order.state in ['purchase', 'done', 'cancel']: + # raise UserError(_("Tidak bisa menambahkan item baru karena PO sudah dikonfirmasi atau selesai.")) + + # return super(PurchaseOrderLine, self).create(vals) + + # def write(self, vals): + # """ + # Override method write untuk membatasi field yang bisa diubah + # jika status PO sudah bukan draft + # """ + # for line in self: + # if line.order_id.state in ['purchase', 'done', 'cancel']: + # # Hanya izinkan mengubah mark_po_line + # if 'mark_po_line' in vals and len(vals) == 1: + # # Izinkan perubahan jika hanya field mark_po_line yang diubah + # return super(PurchaseOrderLine, self).write(vals) + # else: + # _logger.info("Fields being updated: %s", vals.keys()) + # raise UserError(_("Tidak bisa mengubah item karena PO sudah dikonfirmasi atau selesai.")) + + # return super(PurchaseOrderLine, self).write(vals) + + # def toggle_mark_po_line(self): + # """ + # Method khusus untuk toggle mark_po_line, aman digunakan dalam semua status PO + # """ + # for line in self: + # line.mark_po_line = not line.mark_po_line + # return True + + # def unlink(self): + # """ + # Override method unlink untuk mencegah penghapusan line + # jika status PO sudah bukan draft + # """ + # for line in self: + # if line.order_id.state in ['purchase', 'done', 'cancel']: + # raise UserError(_("Tidak bisa menghapus item karena PO sudah dikonfirmasi atau selesai.")) + + # return super(PurchaseOrderLine, self).unlink() + -- cgit v1.2.3 From 351934c0b2107782557998feb68c71cc5ba024e0 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 15 May 2025 13:21:53 +0700 Subject: (andri) hapus method yang tidak diperlukan/comment --- indoteknik_custom/models/purchase_order_line.py | 49 ------------------------- 1 file changed, 49 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 766e304b..4802ebea 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -388,54 +388,5 @@ class PurchaseOrderLine(models.Model): else: line.delivery_amt_line = 0 - # @api.model - # def create(self, vals): - # """ - # Override method create untuk mencegah penambahan line baru - # jika status PO sudah bukan draft - # """ - # # Cek apakah ada order_id di vals - # if vals.get('order_id'): - # order = self.env['purchase.order'].browse(vals['order_id']) - # # Jika status PO bukan draft, tolak pembuatan line baru - # if order.state in ['purchase', 'done', 'cancel']: - # raise UserError(_("Tidak bisa menambahkan item baru karena PO sudah dikonfirmasi atau selesai.")) - - # return super(PurchaseOrderLine, self).create(vals) - - # def write(self, vals): - # """ - # Override method write untuk membatasi field yang bisa diubah - # jika status PO sudah bukan draft - # """ - # for line in self: - # if line.order_id.state in ['purchase', 'done', 'cancel']: - # # Hanya izinkan mengubah mark_po_line - # if 'mark_po_line' in vals and len(vals) == 1: - # # Izinkan perubahan jika hanya field mark_po_line yang diubah - # return super(PurchaseOrderLine, self).write(vals) - # else: - # _logger.info("Fields being updated: %s", vals.keys()) - # raise UserError(_("Tidak bisa mengubah item karena PO sudah dikonfirmasi atau selesai.")) - - # return super(PurchaseOrderLine, self).write(vals) - # def toggle_mark_po_line(self): - # """ - # Method khusus untuk toggle mark_po_line, aman digunakan dalam semua status PO - # """ - # for line in self: - # line.mark_po_line = not line.mark_po_line - # return True - - # def unlink(self): - # """ - # Override method unlink untuk mencegah penghapusan line - # jika status PO sudah bukan draft - # """ - # for line in self: - # if line.order_id.state in ['purchase', 'done', 'cancel']: - # raise UserError(_("Tidak bisa menghapus item karena PO sudah dikonfirmasi atau selesai.")) - - # return super(PurchaseOrderLine, self).unlink() -- cgit v1.2.3 From e5504fd30bcb1490a9de2b4af0e03fb7e22daef1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 13:32:27 +0700 Subject: fix bug --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1c4a6cbc..9b8f6775 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1012,7 +1012,7 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - if self.sale_id.hold_outgoing == True and self.location_id == 57 and self.location_dest_id == 60: + if self.sale_id.hold_outgoing == True and self.location_id.id == 57 and self.location_dest_id.id == 60: raise UserError("SO on hold") res = super(StockPicking, self).action_assign() current_time = datetime.datetime.utcnow() -- cgit v1.2.3 From 07efd05ec1574396ffee0c347e3f92499d174b3e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 13:40:51 +0700 Subject: push disable reserved --- indoteknik_custom/models/sale_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 1242108e..b5f69fa2 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -372,7 +372,11 @@ class SaleOrder(models.Model): if self.hold_outgoing == True: self.hold_outgoing = False else: - pick = self.env['stock.picking'].search([('sale_id', '=', self.id), ('state', 'not in', ['cancel','done']),('BU/PICK/', 'in', self.name)]) + pick = self.env['stock.picking'].search([ + ('sale_id', '=', self.id), + ('state', 'not in', ['cancel', 'done']), + ('name', 'ilike', 'BU/PICK/%') + ]) for picking in pick: picking.do_unreserve() self.hold_outgoing = True -- cgit v1.2.3 From d75cb8d198eeca1296aa467b0d8e3fd9db9c571f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 15 May 2025 15:09:09 +0700 Subject: Add Sales Discount in XML Faktur Pajak --- indoteknik_custom/models/coretax_fatur.py | 82 +++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 25 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index b4bffbd2..ffc2813f 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -4,19 +4,20 @@ from xml.dom import minidom import base64 import re + class CoretaxFaktur(models.Model): _name = 'coretax.faktur' _description = 'Export Faktur ke XML' - - export_file = fields.Binary(string="Export File", ) - export_filename = fields.Char(string="Export File", ) - - def validate_and_format_number(slef, input_number): + + export_file = fields.Binary(string="Export File", ) + export_filename = fields.Char(string="Export File", ) + + def validate_and_format_number(self, input_number): # Hapus semua karakter non-digit cleaned_number = re.sub(r'\D', '', input_number) - + total_sum = sum(int(char) for char in cleaned_number) - if total_sum == 0 : + if total_sum == 0: return '0000000000000000' # Hitung jumlah digit @@ -39,22 +40,16 @@ class CoretaxFaktur(models.Model): # Tambahkan elemen ListOfTaxInvoice list_of_tax_invoice = ET.SubElement(root, 'ListOfTaxInvoice') - # Dapatkan data faktur - # inv_obj = self.env['account.move'] - # invoices = inv_obj.search([('is_efaktur_exported','=',True), - # ('state','=','posted'), - # ('efaktur_id','!=', False), - # ('move_type','=','out_invoice')], limit = 5) - for invoice in invoices: tax_invoice = ET.SubElement(list_of_tax_invoice, 'TaxInvoice') buyerTIN = self.validate_and_format_number(invoice.partner_id.npwp) nitku = invoice.partner_id.nitku formula = nitku if nitku else buyerTIN.ljust(len(buyerTIN) + 6, '0') - buyerIDTKU = formula if sum(int(char) for char in buyerTIN) > 0 else '000000' + buyerIDTKU = formula if sum(int(char) for char in buyerTIN) > 0 else '000000' # Tambahkan elemen faktur - ET.SubElement(tax_invoice, 'TaxInvoiceDate').text = invoice.invoice_date.strftime('%Y-%m-%d') if invoice.invoice_date else '' + ET.SubElement(tax_invoice, 'TaxInvoiceDate').text = invoice.invoice_date.strftime( + '%Y-%m-%d') if invoice.invoice_date else '' ET.SubElement(tax_invoice, 'TaxInvoiceOpt').text = 'Normal' ET.SubElement(tax_invoice, 'TrxCode').text = '04' ET.SubElement(tax_invoice, 'AddInfo') @@ -64,30 +59,67 @@ class CoretaxFaktur(models.Model): ET.SubElement(tax_invoice, 'FacilityStamp') ET.SubElement(tax_invoice, 'SellerIDTKU').text = '0742260227086000000000' ET.SubElement(tax_invoice, 'BuyerTin').text = buyerTIN - ET.SubElement(tax_invoice, 'BuyerDocument').text = 'TIN' if sum(int(char) for char in buyerTIN) > 0 else 'Other ID' + ET.SubElement(tax_invoice, 'BuyerDocument').text = 'TIN' if sum( + int(char) for char in buyerTIN) > 0 else 'Other ID' ET.SubElement(tax_invoice, 'BuyerCountry').text = 'IDN' - ET.SubElement(tax_invoice, 'BuyerDocumentNumber').text = '-' if sum(int(char) for char in buyerTIN) > 0 else str(invoice.partner_id.id) + ET.SubElement(tax_invoice, 'BuyerDocumentNumber').text = '-' if sum( + int(char) for char in buyerTIN) > 0 else str(invoice.partner_id.id) ET.SubElement(tax_invoice, 'BuyerName').text = invoice.partner_id.nama_wajib_pajak or '' ET.SubElement(tax_invoice, 'BuyerAdress').text = invoice.partner_id.alamat_lengkap_text or '' ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or '' ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU + # Find all product lines (exclude discounts and other adjustments) + product_lines = invoice.invoice_line_ids.filtered( + lambda l: not l.display_type and l.product_id and 'Diskon' not in l.account_id.name + ) + + # Find all discount lines + discount_lines = invoice.invoice_line_ids.filtered( + lambda l: not l.display_type and ('Diskon' in l.account_id.name or l.name and 'Diskon' in l.name) + ) + + # Calculate total product amount (before discount) + total_product_amount = sum(line.price_subtotal for line in product_lines) + if total_product_amount == 0: + total_product_amount = 1 # Avoid division by zero + + # Calculate total discount amount + total_discount_amount = abs(sum(line.price_subtotal for line in discount_lines)) + # Tambahkan elemen ListOfGoodService list_of_good_service = ET.SubElement(tax_invoice, 'ListOfGoodService') - for line in invoice.invoice_line_ids: - otherTaxBase = round(line.price_subtotal * (11/12)) if line.price_subtotal else 0 + + for line in product_lines: + # Calculate prorated discount + line_proportion = line.price_subtotal / total_product_amount + line_discount = total_discount_amount * line_proportion + + # unit_price = line.price_unit + subtotal = line.price_subtotal + quantity = line.quantity + total_discount = round(line_discount, 2) + + # Calculate tax base after discount + price_after_discount = line.price_subtotal - line_discount + + # Calculate other tax values + otherTaxBase = round(price_after_discount * (11 / 12), 2) if price_after_discount else 0 + vat_amount = round(otherTaxBase * 0.12, 2) + + # Create the line in XML good_service = ET.SubElement(list_of_good_service, 'GoodService') ET.SubElement(good_service, 'Opt').text = 'A' ET.SubElement(good_service, 'Code').text = '000000' ET.SubElement(good_service, 'Name').text = line.name ET.SubElement(good_service, 'Unit').text = 'UM.0018' - ET.SubElement(good_service, 'Price').text = str(round(line.price_subtotal/line.quantity, 2)) if line.price_subtotal else '0' - ET.SubElement(good_service, 'Qty').text = str(line.quantity) - ET.SubElement(good_service, 'TotalDiscount').text = '0' - ET.SubElement(good_service, 'TaxBase').text = str(round(line.price_subtotal)) if line.price_subtotal else '0' + ET.SubElement(good_service, 'Price').text = str(round(subtotal / quantity, 2)) if subtotal else '0' + ET.SubElement(good_service, 'Qty').text = str(quantity) + ET.SubElement(good_service, 'TotalDiscount').text = str(total_discount) + ET.SubElement(good_service, 'TaxBase').text = str(round(price_after_discount)) ET.SubElement(good_service, 'OtherTaxBase').text = str(otherTaxBase) ET.SubElement(good_service, 'VATRate').text = '12' - ET.SubElement(good_service, 'VAT').text = str(round(otherTaxBase * 0.12, 2)) + ET.SubElement(good_service, 'VAT').text = str(vat_amount) ET.SubElement(good_service, 'STLGRate').text = '0' ET.SubElement(good_service, 'STLG').text = '0' -- cgit v1.2.3 From b8acd5ee5521228eb7c9033d9a234ee2b965db1a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 15 May 2025 16:22:05 +0700 Subject: fix validation mrp bom --- indoteknik_custom/models/sale_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b5f69fa2..b4582f00 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1218,10 +1218,10 @@ class SaleOrder(models.Model): for order in self: for line in order.order_line: 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') + search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id), ('sale_order', '=', order.id), ('state', '!=', 'cancel')], + order='name desc') if search_bom: - confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed') + confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') if not confirmed_bom: raise UserError( "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") -- cgit v1.2.3 From 3ecfd3cbf9e3257644c388801f18870960ef3ac0 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 15 May 2025 21:58:34 +0700 Subject: prevent export error when state is in draft and add filter based on account_id. --- indoteknik_custom/models/coretax_fatur.py | 47 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index ffc2813f..c0558e5b 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -12,11 +12,20 @@ class CoretaxFaktur(models.Model): export_file = fields.Binary(string="Export File", ) export_filename = fields.Char(string="Export File", ) - def validate_and_format_number(self, input_number): + def validate_and_format_number(slef, input_number): + # mencegah error ketika mau cetak xml ketika masih di draft + if input_number is None: + return '0000000000000000' + + # ubah ke str kalau blm + if not isinstance(input_number, str): + input_number = str(input_number) + # Hapus semua karakter non-digit cleaned_number = re.sub(r'\D', '', input_number) total_sum = sum(int(char) for char in cleaned_number) + if total_sum == 0: return '0000000000000000' @@ -69,18 +78,36 @@ class CoretaxFaktur(models.Model): ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or '' ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU - # Find all product lines (exclude discounts and other adjustments) + # initiate diskon id + # ACCOUNT_DISCOUNT_IDS = [463, 464, 467] + # product_lines = invoice.invoice_line_ids.filtered( + # lambda l: not l.display_type and l.product_id and + # hasattr(l, 'account_id') and l.account_id and + # l.account_id.id not in self.ACCOUNT_DISCOUNT_IDS and + # l.quantity != -1 + # ) + # discount_lines = invoice.invoice_line_ids.filtered( + # lambda l: not l.display_type and ( + # (hasattr(l, 'account_id') and l.account_id and + # l.account_id.id in self.ACCOUNT_DISCOUNT_IDS) or + # (l.quantity == -1) + # ) + # ) + # discount_id = self.env['account.account'].search([('name', '=', 'Diskon')]) + + # cari product dari inovoice line product_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and l.product_id and 'Diskon' not in l.account_id.name + lambda l: not l.display_type and l.product_id and 'Diskon' not in l.account_id.id ) - # Find all discount lines + # cari diskon/potongan discount_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and ('Diskon' in l.account_id.name or l.name and 'Diskon' in l.name) - ) + lambda l: not l.display_type and ('Diskon' in l.account_id.id or l.name and 'Diskon' in l.name) + ) # ini ke account.id # Calculate total product amount (before discount) total_product_amount = sum(line.price_subtotal for line in product_lines) + if total_product_amount == 0: total_product_amount = 1 # Avoid division by zero @@ -100,11 +127,8 @@ class CoretaxFaktur(models.Model): quantity = line.quantity total_discount = round(line_discount, 2) - # Calculate tax base after discount - price_after_discount = line.price_subtotal - line_discount - # Calculate other tax values - otherTaxBase = round(price_after_discount * (11 / 12), 2) if price_after_discount else 0 + otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0 vat_amount = round(otherTaxBase * 0.12, 2) # Create the line in XML @@ -116,7 +140,8 @@ class CoretaxFaktur(models.Model): ET.SubElement(good_service, 'Price').text = str(round(subtotal / quantity, 2)) if subtotal else '0' ET.SubElement(good_service, 'Qty').text = str(quantity) ET.SubElement(good_service, 'TotalDiscount').text = str(total_discount) - ET.SubElement(good_service, 'TaxBase').text = str(round(price_after_discount)) + ET.SubElement(good_service, 'TaxBase').text = str( + round(subtotal)) if subtotal else '0' ET.SubElement(good_service, 'OtherTaxBase').text = str(otherTaxBase) ET.SubElement(good_service, 'VATRate').text = '12' ET.SubElement(good_service, 'VAT').text = str(vat_amount) -- cgit v1.2.3 From a6a26665953d36578c62b6f4d5608b716d9fac88 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 15 May 2025 23:42:20 +0700 Subject: fix not iterable account_id --- indoteknik_custom/models/coretax_fatur.py | 40 ++++++++----------------------- 1 file changed, 10 insertions(+), 30 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index c0558e5b..f2a1f793 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -13,14 +13,6 @@ class CoretaxFaktur(models.Model): export_filename = fields.Char(string="Export File", ) def validate_and_format_number(slef, input_number): - # mencegah error ketika mau cetak xml ketika masih di draft - if input_number is None: - return '0000000000000000' - - # ubah ke str kalau blm - if not isinstance(input_number, str): - input_number = str(input_number) - # Hapus semua karakter non-digit cleaned_number = re.sub(r'\D', '', input_number) @@ -78,36 +70,24 @@ class CoretaxFaktur(models.Model): ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or '' ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU - # initiate diskon id - # ACCOUNT_DISCOUNT_IDS = [463, 464, 467] - # product_lines = invoice.invoice_line_ids.filtered( - # lambda l: not l.display_type and l.product_id and - # hasattr(l, 'account_id') and l.account_id and - # l.account_id.id not in self.ACCOUNT_DISCOUNT_IDS and - # l.quantity != -1 - # ) - # discount_lines = invoice.invoice_line_ids.filtered( - # lambda l: not l.display_type and ( - # (hasattr(l, 'account_id') and l.account_id and - # l.account_id.id in self.ACCOUNT_DISCOUNT_IDS) or - # (l.quantity == -1) - # ) - # ) - # discount_id = self.env['account.account'].search([('name', '=', 'Diskon')]) - - # cari product dari inovoice line + # cari product product_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and l.product_id and 'Diskon' not in l.account_id.id + lambda l: not l.display_type and l.product_id and + hasattr(l, 'account_id') and l.account_id and + 'Diskon' not in (l.account_id.name or '') ) # cari diskon/potongan discount_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and ('Diskon' in l.account_id.id or l.name and 'Diskon' in l.name) - ) # ini ke account.id + lambda l: not l.display_type and ( + (hasattr(l, 'account_id') and l.account_id and 'Diskon' in (l.account_id.name or '')) or + (l.name and isinstance(l.name, str) and 'Diskon' in l.name) or + (l.quantity == -1) + ) + ) # Calculate total product amount (before discount) total_product_amount = sum(line.price_subtotal for line in product_lines) - if total_product_amount == 0: total_product_amount = 1 # Avoid division by zero -- cgit v1.2.3 From 24c52892c3143dc2474e5a2ba3c99d08ee1f7d29 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 16 May 2025 08:55:53 +0700 Subject: (andri) add field date hold/unhold SO --- indoteknik_custom/models/sale_order.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0d4fc6c3..0ae61914 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -4,6 +4,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import UserError, ValidationError from datetime import datetime, timedelta import logging, random, string, requests, math, json, re, qrcode, base64 +import pytz from io import BytesIO from collections import defaultdict @@ -302,6 +303,12 @@ class SaleOrder(models.Model): ('hold', 'Hold'), ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) + hold_outgoing_datetime = fields.Datetime( + string='Date Hold/Unhold', + tracking=True, + readonly=True, + help='Waktu terakhir ketika status Hold Outgoing SO berubah' + ) def _compute_total_margin_excl_third_party(self): for order in self: @@ -340,11 +347,13 @@ class SaleOrder(models.Model): } } - def hold_unhold_qty_outgoing_so(self): + def hold_unhold_qty_outgoing_so(self): if self.hold_outgoing == True: self.hold_outgoing = False else: self.hold_outgoing = True + + self.hold_outgoing_datetime = fields.Datetime.now() def _validate_uniform_taxes(self): for order in self: -- cgit v1.2.3 From 4360e1fd9f3af2c18b19463773047d9939716069 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 16 May 2025 09:27:25 +0700 Subject: revisi shipment group --- indoteknik_custom/models/shipment_group.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py index a4bea9c4..48a3fa21 100644 --- a/indoteknik_custom/models/shipment_group.py +++ b/indoteknik_custom/models/shipment_group.py @@ -87,9 +87,6 @@ class ShipmentGroupLine(models.Model): if self.picking_id: picking = self.env['stock.picking'].browse(self.picking_id.id) - 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') @@ -101,9 +98,6 @@ class ShipmentGroupLine(models.Model): 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 -- cgit v1.2.3 From f8811bdf897fe0176921cc01b0ee8c9b98c883d3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 16 May 2025 09:35:23 +0700 Subject: filter using account_id --- indoteknik_custom/models/coretax_fatur.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index f2a1f793..92ff1a72 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -12,6 +12,8 @@ class CoretaxFaktur(models.Model): export_file = fields.Binary(string="Export File", ) export_filename = fields.Char(string="Export File", ) + DISCOUNT_ACCOUNT_ID = 463 + def validate_and_format_number(slef, input_number): # Hapus semua karakter non-digit cleaned_number = re.sub(r'\D', '', input_number) @@ -70,18 +72,19 @@ class CoretaxFaktur(models.Model): ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or '' ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU - # cari product + # Filter product product_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and l.product_id and - hasattr(l, 'account_id') and l.account_id and - 'Diskon' not in (l.account_id.name or '') + lambda l: not l.display_type and hasattr(l, 'account_id') and + l.account_id and l.product_id and + l.account_id.id != self.DISCOUNT_ACCOUNT_ID and + l.quantity != -1 ) - # cari diskon/potongan + # Filter discount discount_lines = invoice.invoice_line_ids.filtered( lambda l: not l.display_type and ( - (hasattr(l, 'account_id') and l.account_id and 'Diskon' in (l.account_id.name or '')) or - (l.name and isinstance(l.name, str) and 'Diskon' in l.name) or + (hasattr(l, 'account_id') and l.account_id and + l.account_id.id == self.DISCOUNT_ACCOUNT_ID) or (l.quantity == -1) ) ) -- cgit v1.2.3 From 76d3d70b10a46e4143d8a2e5e6952341d661fdf2 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 16 May 2025 11:07:18 +0700 Subject: (andri) rev field datetime hold/unhold menjadi 2 field berbeda --- indoteknik_custom/models/sale_order.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0ae61914..17115908 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -303,11 +303,9 @@ class SaleOrder(models.Model): ('hold', 'Hold'), ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) - hold_outgoing_datetime = fields.Datetime( - string='Date Hold/Unhold', - tracking=True, - readonly=True, - help='Waktu terakhir ketika status Hold Outgoing SO berubah' + date_hold = fields.Datetime(string='Date Hold', tracking=True, readonly=True, help='Waktu ketika SO di Hold' + ) + date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' ) def _compute_total_margin_excl_third_party(self): @@ -350,10 +348,11 @@ class SaleOrder(models.Model): def hold_unhold_qty_outgoing_so(self): if self.hold_outgoing == True: self.hold_outgoing = False + self.date_unhold = fields.Datetime.now() else: self.hold_outgoing = True - - self.hold_outgoing_datetime = fields.Datetime.now() + self.date_hold = fields.Datetime.now() + def _validate_uniform_taxes(self): for order in self: -- cgit v1.2.3 From c61cb9fcd3d01b4ffafaa6446f9fd68b09a88ff7 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 16 May 2025 11:17:25 +0700 Subject: (andri) hapus perubahan terkait sync price to SO pada branch ini --- indoteknik_custom/models/purchase_order.py | 228 ------------------------ indoteknik_custom/models/purchase_order_line.py | 2 - 2 files changed, 230 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index d5c08660..98b367d0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -2,7 +2,6 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta -from odoo.tools import float_compare import logging from pytz import timezone, utc import io @@ -90,233 +89,6 @@ class PurchaseOrder(models.Model): store_name = fields.Char(string='Nama Toko') purchase_order_count = fields.Integer('Purchase Order Count', related='partner_id.purchase_order_count') - - - def sync_price_to_so(self): - updated_lines = [] - skipped_lines = [] # Untuk melacak line yang dilewati karena harga sudah sama - affected_so_ids = set() # Untuk melacak SO mana saja yang terkena dampak - - for order in self: - # Filter hanya line-line yang ditandai - marked_lines = order.order_line.filtered(lambda l: l.mark_po_line) - - if not marked_lines: - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': _('Info'), - 'message': _('Tidak ada item yang ditandai untuk disinkronkan'), - 'sticky': False, - 'type': 'info', - } - } - - # Cek apakah ada referensi ke sale order - if not order.sale_order_id and not order.order_sales_match_line: - raise UserError(_("Tidak ada Sales Order yang terkait dengan Purchase Order ini!")) - - # Jika PO dibuat dari SO langsung - if order.sale_order_id: - affected_so_ids.add(order.sale_order_id.id) - for po_line in marked_lines: - # Cari SO line yang terkait - so_line = po_line.so_line_id - if not so_line and po_line.product_id: - # Coba cari berdasarkan product jika tidak ada referensi langsung - so_line = self.env['sale.order.line'].search([ - ('product_id', '=', po_line.product_id.id), - ('order_id', '=', order.sale_order_id.id) - ], limit=1) - - if so_line: - old_price = so_line.purchase_price - - # Cek apakah harga sudah sama - if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0: - skipped_lines.append({ - 'product': po_line.product_id.display_name, - 'so_name': so_line.order_id.name, - 'so_id': so_line.order_id.id, - 'price': old_price, - 'currency': order.currency_id or so_line.order_id.currency_id - }) - else: - so_line.purchase_price = po_line.price_unit - updated_lines.append({ - 'product': po_line.product_id.display_name, - 'so_line_id': so_line.id, - 'so_name': so_line.order_id.name, - 'so_id': so_line.order_id.id, - 'old_price': old_price, - 'new_price': po_line.price_unit, - 'currency': order.currency_id or so_line.order_id.currency_id - }) - - # Hapus tanda setelah sinkronisasi - po_line.mark_po_line = False - - # Jika PO terkait dengan beberapa SO (melalui order_sales_match_line) - elif order.order_sales_match_line: - for po_line in marked_lines: - # Cari match lines yang sesuai dengan product di PO line - match_lines = order.order_sales_match_line.filtered( - lambda m: m.product_id.id == po_line.product_id.id - ) - - for match_line in match_lines: - if match_line.sale_id: - affected_so_ids.add(match_line.sale_id.id) - - so_line = match_line.sale_line_id - if so_line: - old_price = so_line.purchase_price - - # Cek apakah harga sudah sama - if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0: - skipped_lines.append({ - 'product': po_line.product_id.display_name, - 'so_name': so_line.order_id.name, - 'so_id': so_line.order_id.id, - 'price': old_price, - 'currency': order.currency_id or so_line.order_id.currency_id - }) - else: - so_line.purchase_price = po_line.price_unit - updated_lines.append({ - 'product': po_line.product_id.display_name, - 'so_line_id': so_line.id, - 'so_name': so_line.order_id.name, - 'so_id': so_line.order_id.id, - 'old_price': old_price, - 'new_price': po_line.price_unit, - 'currency': order.currency_id or so_line.order_id.currency_id - }) - - # Hapus tanda setelah sinkronisasi - po_line.mark_po_line = False - - # Ambil data SO yang terkena dampak - affected_sales_orders = self.env['sale.order'].browse(list(affected_so_ids)) - - # Buat pesan untuk log - message_body = "" - - # Jika ada line yang diupdate - if updated_lines: - # Kelompokkan perubahan berdasarkan SO - changes_by_so = {} - for line in updated_lines: - so_id = line['so_id'] - if so_id not in changes_by_so: - changes_by_so[so_id] = [] - changes_by_so[so_id].append(line) - - # Buat pesan untuk line yang diupdate - message_body += f"

    Harga Purchase pada Sales Order telah diperbarui dari {self.name}:

    " - - for so_id, lines in changes_by_so.items(): - so = self.env['sale.order'].browse(so_id) - # Buat link ke SO yang bisa diklik - message_body += f"

    Sales Order: {so.name}

      " - for line in lines: - # Format harga dalam format mata uang - currency = line['currency'] - old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency}) - new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency}) - - message_body += f"
    • {line['product']}:
      {old_price_formatted} → {new_price_formatted}
    • " - message_body += "
    " - - # Jika ada line yang dilewati karena harga sudah sama - if skipped_lines: - # Kelompokkan berdasarkan SO - skipped_by_so = {} - for line in skipped_lines: - so_id = line['so_id'] - if so_id not in skipped_by_so: - skipped_by_so[so_id] = [] - skipped_by_so[so_id].append(line) - - # Tambahkan pesan untuk line yang dilewati - if message_body: - message_body += "

    Item berikut dilewati karena harga sudah sama:

    " - else: - message_body += f"

    Tidak ada perubahan harga untuk PO {self.name}:

    " - message_body += "

    Item berikut sudah memiliki harga yang sama di SO:

    " - - for so_id, lines in skipped_by_so.items(): - so = self.env['sale.order'].browse(so_id) - message_body += f"

    Sales Order: {so.name}

      " - for line in lines: - # Format harga dalam format mata uang - currency = line['currency'] - price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['price'], {'display_currency': currency}) - - message_body += f"
    • {line['product']}: {price_formatted}
    • " - message_body += "
    " - - # Posting log message jika ada isi - if message_body: - subject = "Price Sync to SO" if updated_lines else "Price Sync - Harga Sama" - self.message_post(body=message_body, subject=subject) - - # Update juga log di setiap SO yang terkena dampak - if updated_lines: - for so in affected_sales_orders: - so_lines = [line for line in updated_lines if line['so_id'] == so.id] - if so_lines: - # Buat link ke PO yang bisa diklik - so_message = f"

    Harga Purchase diperbarui dari {self.name}:

      " - for line in so_lines: - # Format harga dalam format mata uang - currency = line['currency'] - old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency}) - new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency}) - - so_message += f"
    • {line['product']}:
      {old_price_formatted} → {new_price_formatted}
    • " - so_message += "
    " - so.message_post(body=so_message, subject=f"Price Updated from PO {self.name}") - - # Recalculate margins - if updated_lines and hasattr(self, 'compute_total_margin'): - self.compute_total_margin() - - # Recalculate margins di SO juga - if updated_lines: - for so in affected_sales_orders: - if hasattr(so, 'compute_total_margin'): - so.compute_total_margin() - - # Tentukan pesan notifikasi dan tipe - if updated_lines and skipped_lines: - message = f"{len(updated_lines)} item diperbarui dan {len(skipped_lines)} item dilewati karena harga sudah sama" - title = "Sukses!" - notification_type = "success" - elif updated_lines: - message = f"{len(updated_lines)} item telah diperbarui harganya di {len(affected_so_ids)} Sales Order" - title = "Sukses!" - notification_type = "success" - elif skipped_lines: - message = f"item tersebut ({len(skipped_lines)}) sudah memiliki harga yang sama" - title = "Info" - notification_type = "info" - else: - message = "Tidak ada line yang berhasil diperbarui" - title = "Info" - notification_type = "info" - - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': _(title), - 'message': _(message), - 'sticky': False, - 'type': notification_type, - } - } # cek payment term def _check_payment_term(self): _logger.info("Check Payment Term Terpanggil") diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 4802ebea..315795d5 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -51,8 +51,6 @@ class PurchaseOrderLine(models.Model): contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt') ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt') - mark_po_line = fields.Boolean(string=' ', default=False, help='centang jika anda ingin menandai PO line ini') - def _compute_doc_delivery_amt(self): for line in self: # Inisialisasi nilai default untuk field computed -- cgit v1.2.3 From b1603152659e81ba005e580350704f54d0aaadf5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 17 May 2025 11:05:44 +0700 Subject: add user error changing payment term when state is approved --- indoteknik_custom/models/sale_order.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b4582f00..6d33af7c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -7,6 +7,8 @@ import logging, random, string, requests, math, json, re, qrcode, base64 from io import BytesIO from collections import defaultdict +from psutil import users + _logger = logging.getLogger(__name__) @@ -1146,6 +1148,15 @@ class SaleOrder(models.Model): return helper_ids_str.split(', ') def write(self, values): + if 'paymente_term_id' in values: + for record in self: + if record.payment_term_id not in ['cancel', + 'draft'] and record.approval_status == 'approved': + if self.env.user.has_group( + 'sales_team.group_sale_salesman') and not self.env.user.has_group( + 'sales_team.group_sale_manager'): + raise UserError('Tidak bisa mengganti payment term karena sudah approved') + helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: values['helper_by_id'] = self.env.user.id @@ -1218,10 +1229,12 @@ class SaleOrder(models.Model): for order in self: for line in order.order_line: 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), ('sale_order', '=', order.id), ('state', '!=', 'cancel')], - order='name desc') + search_bom = self.env['mrp.production'].search( + [('product_id', '=', line.product_id.id), ('sale_order', '=', order.id), + ('state', '!=', 'cancel')], + order='name desc') if search_bom: - confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') + confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') if not confirmed_bom: raise UserError( "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") @@ -1519,7 +1532,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if self.env.context.get('due_approve', []) == False: if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', -- cgit v1.2.3 From a9b205f38697c53410a2859aff7b2d79acc9dcd0 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 17 May 2025 11:06:37 +0700 Subject: fix typo --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4202626f..71bbd648 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1156,7 +1156,7 @@ class SaleOrder(models.Model): return helper_ids_str.split(', ') def write(self, values): - if 'paymente_term_id' in values: + if 'payment_term_id' in values: for record in self: if record.payment_term_id not in ['cancel', 'draft'] and record.approval_status == 'approved': -- cgit v1.2.3 From cb1e9095e79faddd7d2f006a7c7700bff37076fa Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Sat, 17 May 2025 12:04:31 +0700 Subject: fix bug due extension --- indoteknik_custom/models/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b4582f00..66d7d148 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1271,7 +1271,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - if self.env.context.get('due_approve', []) == False: + if not self.env.context.get('due_approve', []): if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') @@ -1520,7 +1520,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - if self.env.context.get('due_approve', []) == False: + if not self.env.context.get('due_approve', []): if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') -- cgit v1.2.3 From 2c2a1ac7fe9530390b4bbc473d629ddfb2b97d2b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 17 May 2025 13:26:59 +0700 Subject: remove permission for sales manager to edit --- indoteknik_custom/models/sale_order.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 71bbd648..57a5000e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1160,10 +1160,7 @@ class SaleOrder(models.Model): for record in self: if record.payment_term_id not in ['cancel', 'draft'] and record.approval_status == 'approved': - if self.env.user.has_group( - 'sales_team.group_sale_salesman') and not self.env.user.has_group( - 'sales_team.group_sale_manager'): - raise UserError('Tidak bisa mengganti payment term karena sudah approved') + raise UserError('Tidak bisa mengganti payment term karena sudah approved') helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: -- cgit v1.2.3 From 434260b95902e088820c3b5d14d7a565f01a21c1 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 17 May 2025 13:55:59 +0700 Subject: fix error --- indoteknik_custom/models/sale_order.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 57a5000e..8a57b9c0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -334,9 +334,9 @@ class SaleOrder(models.Model): ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) date_hold = fields.Datetime(string='Date Hold', tracking=True, readonly=True, help='Waktu ketika SO di Hold' - ) + ) date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' - ) + ) def _compute_total_margin_excl_third_party(self): for order in self: @@ -375,7 +375,7 @@ class SaleOrder(models.Model): } } - def hold_unhold_qty_outgoing_so(self): + def hold_unhold_qty_outgoing_so(self): if self.hold_outgoing == True: self.hold_outgoing = False self.date_unhold = fields.Datetime.now() @@ -389,7 +389,6 @@ class SaleOrder(models.Model): picking.do_unreserve() self.hold_outgoing = True self.date_hold = fields.Datetime.now() - def _validate_uniform_taxes(self): for order in self: @@ -1157,9 +1156,9 @@ class SaleOrder(models.Model): def write(self, values): if 'payment_term_id' in values: - for record in self: - if record.payment_term_id not in ['cancel', - 'draft'] and record.approval_status == 'approved': + if self.state not in ['cancel', 'draft'] and self.approval_status == 'approved': + if self.env.user.has_group('sales_team.group_sale_salesman') or self.env.user.has_group( + 'sales_team.group_sale_manager'): raise UserError('Tidak bisa mengganti payment term karena sudah approved') helper_ids = self._get_helper_ids() -- cgit v1.2.3 From 434bf64f3229b12ec938671d74446389cb0455aa Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sun, 18 May 2025 01:50:35 +0700 Subject: prevent sales member or sales manager to change payment term when so/approved --- indoteknik_custom/models/sale_order.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8a57b9c0..bfb38f6d 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1032,12 +1032,6 @@ class SaleOrder(models.Model): line_no += 1 line.line_no = line_no - def write(self, vals): - if 'carrier_id' in vals: - for picking in self.picking_ids: - if picking.state == 'assigned': - picking.carrier_id = self.carrier_id - def calculate_so_status(self): so_state = ['sale'] sales = self.search([ @@ -1154,8 +1148,13 @@ class SaleOrder(models.Model): helper_ids_str = self.env['ir.config_parameter'].sudo().get_param('sale.order.user_helper_ids') return helper_ids_str.split(', ') - def write(self, values): - if 'payment_term_id' in values: + def write(self, vals): + if 'carrier_id' in vals: + for picking in self.picking_ids: + if picking.state == 'assigned': + picking.carrier_id = self.carrier_id + + if 'payment_term_id' in vals: if self.state not in ['cancel', 'draft'] and self.approval_status == 'approved': if self.env.user.has_group('sales_team.group_sale_salesman') or self.env.user.has_group( 'sales_team.group_sale_manager'): @@ -1163,9 +1162,9 @@ class SaleOrder(models.Model): helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: - values['helper_by_id'] = self.env.user.id + vals['helper_by_id'] = self.env.user.id - return super(SaleOrder, self).write(values) + return super(SaleOrder, self).write(vals) def check_due(self): """To show the due amount and warning stage""" -- cgit v1.2.3 From abb5b01c3bbaf19feaf1be8f3a3fac6b95a1d6a6 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Mon, 19 May 2025 15:06:58 +0700 Subject: bf qty delivered minus --- indoteknik_custom/models/stock_picking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 9b8f6775..128efee6 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1012,12 +1012,12 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - if self.sale_id.hold_outgoing == True and self.location_id.id == 57 and self.location_dest_id.id == 60: + if self.sale_id.hold_outgoing and self.location_id.id == 57 and self.location_dest_id.id == 60: raise UserError("SO on hold") res = super(StockPicking, self).action_assign() - current_time = datetime.datetime.utcnow() - self.real_shipping_id = self.sale_id.real_shipping_id - self.date_availability = current_time + # current_time = datetime.datetime.utcnow() + # self.real_shipping_id = self.sale_id.real_shipping_id + # self.date_availability = current_time # self.check_state_reserve() return res -- cgit v1.2.3 From 62088f63cc7904c07fa3e95d7dd487fb9cd4926c Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Mon, 19 May 2025 15:54:45 +0700 Subject: bf minus delivered qty --- indoteknik_custom/models/sale_order_line.py | 13 +++++++++++++ indoteknik_custom/models/stock_picking.py | 13 +++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 2450abd4..049bee2d 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -41,6 +41,19 @@ class SaleOrderLine(models.Model): qty_free_bu = fields.Float(string='Free BU', compute='_get_qty_free_bandengan') desc_updatable = fields.Boolean(string='desc boolean', default=True, compute='_get_desc_updatable') + def _get_outgoing_incoming_moves(self): + outgoing_moves = self.env['stock.move'] + incoming_moves = self.env['stock.move'] + + for move in self.move_ids.filtered(lambda r: r.state != 'cancel' and not r.scrapped and self.product_id == r.product_id): + if move.location_dest_id.usage == "customer": + if not move.origin_returned_move_id or (move.origin_returned_move_id and move.to_refund): + outgoing_moves |= move + elif move.location_id.usage == "customer" and move.to_refund: + incoming_moves |= move + + return outgoing_moves, incoming_moves + def _get_desc_updatable(self): for line in self: if line.product_id.id != 417724 and line.product_id.id: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 128efee6..0071bb1a 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1013,13 +1013,14 @@ class StockPicking(models.Model): def action_assign(self): if self.sale_id.hold_outgoing and self.location_id.id == 57 and self.location_dest_id.id == 60: - raise UserError("SO on hold") - res = super(StockPicking, self).action_assign() - # current_time = datetime.datetime.utcnow() - # self.real_shipping_id = self.sale_id.real_shipping_id - # self.date_availability = current_time + print(1) + else: + res = super(StockPicking, self).action_assign() + current_time = datetime.datetime.utcnow() + self.real_shipping_id = self.sale_id.real_shipping_id + self.date_availability = current_time # self.check_state_reserve() - return res + return res def ask_approval(self): if self.env.user.is_accounting: -- cgit v1.2.3 From be8056d1b69d4795724f164958171018d9968f46 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 20 May 2025 05:23:26 +0700 Subject: set payment term readonly when on any approval process --- indoteknik_custom/models/sale_order.py | 40 +++++++++++++++------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index bbd2cd71..c7e713d0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -8,8 +8,6 @@ import pytz from io import BytesIO from collections import defaultdict -from psutil import users - _logger = logging.getLogger(__name__) @@ -334,9 +332,9 @@ class SaleOrder(models.Model): ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) date_hold = fields.Datetime(string='Date Hold', tracking=True, readonly=True, help='Waktu ketika SO di Hold' - ) + ) date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' - ) + ) def _compute_total_margin_excl_third_party(self): for order in self: @@ -389,7 +387,6 @@ class SaleOrder(models.Model): picking.do_unreserve() self.hold_outgoing = True self.date_hold = fields.Datetime.now() - def _validate_uniform_taxes(self): for order in self: @@ -407,8 +404,8 @@ class SaleOrder(models.Model): # if rec.fee_third_party == 0 and rec.total_margin_excl_third_party != rec.total_percent_margin: # # Gunakan direct SQL atau flag context untuk menghindari rekursi # self.env.cr.execute(""" - # UPDATE sale_order - # SET total_margin_excl_third_party = %s + # UPDATE sale_order + # SET total_margin_excl_third_party = %s # WHERE id = %s # """, (rec.total_percent_margin, rec.id)) # self.invalidate_cache() @@ -1155,23 +1152,12 @@ class SaleOrder(models.Model): helper_ids_str = self.env['ir.config_parameter'].sudo().get_param('sale.order.user_helper_ids') return helper_ids_str.split(', ') - def write(self, vals): - if 'carrier_id' in vals: - for picking in self.picking_ids: - if picking.state == 'assigned': - picking.carrier_id = self.carrier_id - - if 'payment_term_id' in vals: - if self.state not in ['cancel', 'draft'] and self.approval_status == 'approved': - if self.env.user.has_group('sales_team.group_sale_salesman') or self.env.user.has_group( - 'sales_team.group_sale_manager'): - raise UserError('Tidak bisa mengganti payment term karena sudah approved') - + def write(self, values): helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: - vals['helper_by_id'] = self.env.user.id + values['helper_by_id'] = self.env.user.id - return super(SaleOrder, self).write(vals) + return super(SaleOrder, self).write(values) def check_due(self): """To show the due amount and warning stage""" @@ -1542,7 +1528,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if not self.env.context.get('due_approve', []): if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', @@ -2023,3 +2009,13 @@ class SaleOrder(models.Model): if any(field in vals for field in ["order_line", "client_order_ref"]): self._calculate_etrts_date() return res + + def write(self, vals): + # Cek apakah payment_term_id diubah dan SO sedang dalam approval + if 'payment_term_id' in vals and any( + order.approval_status in ['pengajuan1', 'pengajuan2', 'approved'] for order in self): + raise UserError( + "Payment Term tidak dapat diubah karena Sales Order sedang dalam proses approval atau sudah diapprove.") + + # Lanjutkan dengan logika write yang sudah ada + return super(SaleOrder, self).write(vals) -- cgit v1.2.3 From accaff72daae402924f89c2347b57b75901f542b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 20 May 2025 10:09:16 +0700 Subject: if payment term != customer payment term cannot save --- indoteknik_custom/models/sale_order.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index c7e713d0..e977e07c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2017,5 +2017,17 @@ class SaleOrder(models.Model): raise UserError( "Payment Term tidak dapat diubah karena Sales Order sedang dalam proses approval atau sudah diapprove.") - # Lanjutkan dengan logika write yang sudah ada + if 'payment_term_id' in vals: + for order in self: + partner = order.partner_id.parent_id or order.partner_id + customer_payment_term = partner.property_payment_term_id + + # Jika payment term yang diinput berbeda dengan payment term customer + if vals['payment_term_id'] != customer_payment_term.id: + raise UserError( + f"Payment Term berbeda pada Master Data Customer. " + f"Harap ganti ke '{customer_payment_term.name}' " + f"sesuai dengan payment term yang terdaftar pada customer." + ) + return super(SaleOrder, self).write(vals) -- cgit v1.2.3 From c8d4530d4ade041378691613acd642963bd84c31 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 21 May 2025 15:23:40 +0700 Subject: trying to fix bug matches so po --- indoteknik_custom/models/automatic_purchase.py | 45 +++++++++++++++++++------- 1 file changed, 33 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index b66121e1..ff10b814 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -696,21 +696,42 @@ class SaleNotInMatchPO(models.Model): purchase_tax_id = fields.Many2one('account.tax', string='Purchase Tax') note_procurement = fields.Many2one(string='Note Procurement') + # 1. yang bug + # 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 + # join purchase_order po on po.id = posm.purchase_order_id + # where po.state not in ('cancel') + # ) + # ) + # """ % self._table) + 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 - join purchase_order po on po.id = posm.purchase_order_id - where po.state not in ('cancel') + 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 posm.sale_line_id + FROM purchase_order_sales_match posm + JOIN purchase_order po ON po.id = posm.purchase_order_id + WHERE po.state NOT IN ('cancel') ) ) - """ % self._table) + """ % self._table) \ No newline at end of file -- cgit v1.2.3 From 1047274c07dc302e9c6b3079e6ba7cc9349a9bd0 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 22 May 2025 11:50:12 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0071bb1a..87255ab6 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -584,6 +584,7 @@ class StockPicking(models.Model): self.lalamove_phone = phone self.lalamove_status = pod.get("status") self.lalamove_delivered_at = delivered_at_dt + self.driver_arrival_date = delivered_at_dt return data raise UserError("No delivered data found in Lalamove response.") -- cgit v1.2.3 From cbf9eff4c2f934491b3a1adbf56255c14c4852ea Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 22 May 2025 14:00:28 +0700 Subject: push --- indoteknik_custom/models/sale_advance_payment_inv.py | 1 + indoteknik_custom/models/stock_picking.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_advance_payment_inv.py b/indoteknik_custom/models/sale_advance_payment_inv.py index bea9a900..48b753cc 100644 --- a/indoteknik_custom/models/sale_advance_payment_inv.py +++ b/indoteknik_custom/models/sale_advance_payment_inv.py @@ -197,6 +197,7 @@ class SaleAdvancePaymentInv(models.TransientModel): # if final: # moves.sudo().filtered(lambda m: m.amount_total < 0).action_switch_invoice_into_refund_credit_note() # for move in moves: + # move.message_post_with_view('mail.message_origin_link', # values={'self': move, 'origin': move.line_ids.mapped('sale_line_ids.order_id')}, # subtype_id=self.env.ref('mail.mt_note').id diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 87255ab6..3135f41c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -288,7 +288,7 @@ class StockPicking(models.Model): self.ensure_one() if not self.name or not self.origin: return False - return f"{self.name} {self.origin}" + return f"{self.name}, {self.origin}" def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" -- cgit v1.2.3 From 558130bbf48c33ddfa6080450c80bc8801a570f0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 22 May 2025 15:34:03 +0700 Subject: form-merchant & quotation tampilan --- indoteknik_custom/models/sale_order.py | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0d4fc6c3..6ccb6fde 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -303,6 +303,12 @@ class SaleOrder(models.Model): ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) + ready_to_ship_status_detail = fields.Char( + string='Status Shipping Detail', + compute='_compute_ready_to_ship_status_detail' + ) + + def _compute_total_margin_excl_third_party(self): for order in self: if order.amount_untaxed == 0: @@ -1939,4 +1945,39 @@ class SaleOrder(models.Model): self._validate_delivery_amt() if any(field in vals for field in ["order_line", "client_order_ref"]): self._calculate_etrts_date() - return res \ No newline at end of file + return res + + # @api.depends('commitment_date') + def _compute_ready_to_ship_status_detail(self): + for order in self: + eta = order.commitment_date + + match_lines = self.env['purchase.order.sales.match'].search([ + ('sale_id', '=', order.id) + ]) + + if match_lines: + for match in match_lines: + po = match.purchase_order_id + product = match.product_id + + po_line = self.env['purchase.order.line'].search([ + ('order_id', '=', po.id), + ('product_id', '=', product.id) + ], limit=1) + + stock_move = self.env['stock.move'].search([ + ('purchase_line_id', '=', po_line.id) + ], limit=1) + picking_in = stock_move.picking_id + + result_date = picking_in.date_done if picking_in else None + if result_date: + status = "Early" if result_date < eta else "Delay" + result_date_str = result_date.strftime('%m/%d/%Y') + eta_str = eta.strftime('%m/%d/%Y') + order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}" + else: + order.ready_to_ship_status_detail = "On Track" + else: + order.ready_to_ship_status_detail = 'On Track' \ No newline at end of file -- cgit v1.2.3 From cc6e4b9e48752ee54b42db2c16fae7cd5d5af1c4 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:02:14 +0700 Subject: quick fix purchasing job conflict with manufacturing order sync to po --- indoteknik_custom/models/automatic_purchase.py | 4 ++-- indoteknik_custom/models/product_template.py | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index ff10b814..518dc74c 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -728,10 +728,10 @@ class SaleNotInMatchPO(models.Model): apsm.purchase_tax_id, apsm.note_procurement FROM automatic_purchase_sales_match apsm WHERE apsm.sale_line_id NOT IN ( - SELECT posm.sale_line_id + SELECT distinct coalesce(posm.sale_line_id,0) FROM purchase_order_sales_match posm JOIN purchase_order po ON po.id = posm.purchase_order_id WHERE po.state NOT IN ('cancel') ) ) - """ % self._table) \ No newline at end of file + """ % self._table) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 89392033..6903b53e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -913,7 +913,8 @@ class ProductProduct(models.Model): qty_onhand_bandengan = fields.Float(string='Onhand BU', compute='_get_qty_onhand_bandengan') clean_website_description = fields.Char(string='Clean Website Description', compute='_get_clean_website_description') qty_incoming_bandengan = fields.Float(string='Incoming BU', compute='_get_qty_incoming_bandengan') - qty_outgoing_bandengan = fields.Float(string='Outgoing BU', compute='_get_qty_outgoing_bandengan') + qty_outgoing_bandengan = fields.Float(string='Outgoing BU', compute='_get_qty_outgoing_bandengan', help='only outgoing from sales order bandengan') + qty_outgoing_mo_bandengan = fields.Float(string='Outgoing MO BU', compute='_get_qty_outgoing_mo_bandengan', help='only outgoing from manufacturing order bandengan') qty_available_bandengan = fields.Float(string='Available BU', compute='_get_qty_available_bandengan') qty_free_bandengan = fields.Float(string='Free BU', compute='_get_qty_free_bandengan') qty_upcoming = fields.Float(string='Qty Upcoming', compute='_get_qty_upcoming') @@ -1115,12 +1116,23 @@ class ProductProduct(models.Model): domain=[ ('product_id', '=', product.id), ('location_id', 'in', [57, 83]), + ('mo_id', '=', False) ], fields=['qty_need'], groupby=[] )[0].get('qty_need', 0.0) product.qty_outgoing_bandengan = qty + def _get_qty_outgoing_mo_bandengan(self): + for product in self: + records = self.env['v.move.outstanding'].search([ + ('product_id.id', '=', product.id), + ('location_id.id', 'in', [57, 83]), + ('mo_id.id', '>', 0) + ]) + qty = sum(records.mapped('qty_need') or [0.0]) + product.qty_outgoing_mo_bandengan = qty + def _get_qty_onhand_bandengan(self): for product in self: qty_onhand = self.env['stock.quant'].search([ @@ -1132,7 +1144,7 @@ class ProductProduct(models.Model): def _get_qty_available_bandengan(self): for product in self: - qty_available = product.qty_incoming_bandengan + product.qty_onhand_bandengan - product.qty_outgoing_bandengan + qty_available = product.qty_incoming_bandengan + product.qty_onhand_bandengan - product.qty_outgoing_bandengan - product.qty_outgoing_mo_bandengan product.qty_available_bandengan = qty_available or 0 def _get_qty_free_bandengan(self): @@ -1334,6 +1346,7 @@ class ProductProduct(models.Model): else: return super().write(vals) + class OutstandingMove(models.Model): _name = 'v.move.outstanding' _auto = False @@ -1345,6 +1358,7 @@ class OutstandingMove(models.Model): qty_need = fields.Float(string='Qty Need', help='Qty yang akan outgoing / incoming') location_id = fields.Many2one('stock.location', string='Location', help='Lokasi asal') location_dest_id = fields.Many2one('stock.location', string='Location To', help='Lokasi tujuan') + mo_id = fields.Many2one('mrp.production', string='Manufacturing Order') def init(self): # where clause 'state in' follow the origin of outgoing and incoming odoo @@ -1353,7 +1367,8 @@ class OutstandingMove(models.Model): CREATE OR REPLACE VIEW %s AS select sm.id, sm.reference, sm.product_id, sm.product_uom_qty as qty_need, - sm.location_id, sm.location_dest_id + sm.location_id, sm.location_dest_id, + sm.raw_material_production_id as mo_id from stock_move sm where 1=1 and sm.state in( -- cgit v1.2.3 From 46e4c80e1b530274c01cca5603f3e1be873820f6 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:21:45 +0700 Subject: bf cant create invoice cause of custom method action assign wrong implement --- indoteknik_custom/models/stock_picking.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3135f41c..504338b0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1013,15 +1013,15 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - if self.sale_id.hold_outgoing and self.location_id.id == 57 and self.location_dest_id.id == 60: - print(1) - else: - res = super(StockPicking, self).action_assign() + res = super(StockPicking, self).action_assign() + for move in self: + # if not move.sale_id.hold_outgoing and move.location_id.id != 57 and move.location_dest_id.id != 60: + # TODO cant skip hold outgoing cause of not singleton method current_time = datetime.datetime.utcnow() - self.real_shipping_id = self.sale_id.real_shipping_id - self.date_availability = current_time + move.real_shipping_id = move.sale_id.real_shipping_id + move.date_availability = current_time # self.check_state_reserve() - return res + return res def ask_approval(self): if self.env.user.is_accounting: -- cgit v1.2.3 From 4fc520a9b0d1e2e809033b35562fd80fffad472d Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:37:45 +0700 Subject: Revert "bf cant create invoice cause of custom method action assign wrong implement" This reverts commit 46e4c80e1b530274c01cca5603f3e1be873820f6. --- indoteknik_custom/models/stock_picking.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 504338b0..3135f41c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1013,15 +1013,15 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - res = super(StockPicking, self).action_assign() - for move in self: - # if not move.sale_id.hold_outgoing and move.location_id.id != 57 and move.location_dest_id.id != 60: - # TODO cant skip hold outgoing cause of not singleton method + if self.sale_id.hold_outgoing and self.location_id.id == 57 and self.location_dest_id.id == 60: + print(1) + else: + res = super(StockPicking, self).action_assign() current_time = datetime.datetime.utcnow() - move.real_shipping_id = move.sale_id.real_shipping_id - move.date_availability = current_time + self.real_shipping_id = self.sale_id.real_shipping_id + self.date_availability = current_time # self.check_state_reserve() - return res + return res def ask_approval(self): if self.env.user.is_accounting: -- cgit v1.2.3 From dbf09bbf792809e84d2d0330a992e42b3a5c6994 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:38:02 +0700 Subject: Revert "Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into odoo-backup" This reverts commit 43313db30da73b87843425c01c723f66ee982886, reversing changes made to f6f59e660af6c4229ada54f7313d68867df1ba15. --- indoteknik_custom/models/automatic_purchase.py | 45 ++++++--------------- indoteknik_custom/models/product_template.py | 6 +-- .../models/sale_advance_payment_inv.py | 1 - indoteknik_custom/models/sale_order.py | 46 +++++----------------- indoteknik_custom/models/stock_picking.py | 3 +- 5 files changed, 26 insertions(+), 75 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index ff10b814..b66121e1 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -696,42 +696,21 @@ class SaleNotInMatchPO(models.Model): purchase_tax_id = fields.Many2one('account.tax', string='Purchase Tax') note_procurement = fields.Many2one(string='Note Procurement') - # 1. yang bug - # 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 - # join purchase_order po on po.id = posm.purchase_order_id - # where po.state not in ('cancel') - # ) - # ) - # """ % self._table) - 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 posm.sale_line_id - FROM purchase_order_sales_match posm - JOIN purchase_order po ON po.id = posm.purchase_order_id - WHERE po.state NOT IN ('cancel') + 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 + join purchase_order po on po.id = posm.purchase_order_id + where po.state not in ('cancel') ) ) - """ % self._table) \ No newline at end of file + """ % self._table) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 89392033..17805c6c 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -870,9 +870,9 @@ class ProductTemplate(models.Model): record.message_post(body=f"Updated:
      {''.join(changes)}
    ") # log changes to product variants - # variant_message = f"Updated:
      {''.join(changes)}
    " - # for variant in record.product_variant_ids: - # variant.message_post(body=variant_message) + variant_message = f"Updated:
      {''.join(changes)}
    " + for variant in record.product_variant_ids: + variant.message_post(body=variant_message) # simpan data lama dan log perubahan field def write(self, vals): diff --git a/indoteknik_custom/models/sale_advance_payment_inv.py b/indoteknik_custom/models/sale_advance_payment_inv.py index 48b753cc..bea9a900 100644 --- a/indoteknik_custom/models/sale_advance_payment_inv.py +++ b/indoteknik_custom/models/sale_advance_payment_inv.py @@ -197,7 +197,6 @@ class SaleAdvancePaymentInv(models.TransientModel): # if final: # moves.sudo().filtered(lambda m: m.amount_total < 0).action_switch_invoice_into_refund_credit_note() # for move in moves: - # move.message_post_with_view('mail.message_origin_link', # values={'self': move, 'origin': move.line_ids.mapped('sale_line_ids.order_id')}, # subtype_id=self.env.ref('mail.mt_note').id diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index db0e1227..6028a1d0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -332,9 +332,9 @@ class SaleOrder(models.Model): ('approve', 'Approve') ], tracking=True, string='State Cancel', copy=False) date_hold = fields.Datetime(string='Date Hold', tracking=True, readonly=True, help='Waktu ketika SO di Hold' - ) + ) date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' - ) + ) ready_to_ship_status_detail = fields.Char( string='Status Shipping Detail', @@ -379,7 +379,7 @@ class SaleOrder(models.Model): } } - def hold_unhold_qty_outgoing_so(self): + def hold_unhold_qty_outgoing_so(self): if self.hold_outgoing == True: self.hold_outgoing = False self.date_unhold = fields.Datetime.now() @@ -393,6 +393,7 @@ class SaleOrder(models.Model): picking.do_unreserve() self.hold_outgoing = True self.date_hold = fields.Datetime.now() + def _validate_uniform_taxes(self): for order in self: @@ -410,8 +411,8 @@ class SaleOrder(models.Model): # if rec.fee_third_party == 0 and rec.total_margin_excl_third_party != rec.total_percent_margin: # # Gunakan direct SQL atau flag context untuk menghindari rekursi # self.env.cr.execute(""" - # UPDATE sale_order - # SET total_margin_excl_third_party = %s + # UPDATE sale_order + # SET total_margin_excl_third_party = %s # WHERE id = %s # """, (rec.total_percent_margin, rec.id)) # self.invalidate_cache() @@ -1231,12 +1232,10 @@ class SaleOrder(models.Model): for order in self: for line in order.order_line: 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), ('sale_order', '=', order.id), - ('state', '!=', 'cancel')], - order='name desc') + search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id), ('sale_order', '=', order.id), ('state', '!=', 'cancel')], + order='name desc') if search_bom: - confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') + confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') if not confirmed_bom: raise UserError( "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") @@ -1534,7 +1533,7 @@ class SaleOrder(models.Model): if not order.real_shipping_id: UserError('Real Delivery Address harus di isi') - + if not self.env.context.get('due_approve', []): if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', @@ -2016,7 +2015,6 @@ class SaleOrder(models.Model): <<<<<<< HEAD self._calculate_etrts_date() return res -<<<<<<< HEAD ======= self._calculate_etrts_date() return res @@ -2056,27 +2054,3 @@ class SaleOrder(models.Model): else: order.ready_to_ship_status_detail = 'On Track' >>>>>>> CR/form-merchant -======= - - def write(self, vals): - # Cek apakah payment_term_id diubah dan SO sedang dalam approval - if 'payment_term_id' in vals and any( - order.approval_status in ['pengajuan1', 'pengajuan2', 'approved'] for order in self): - raise UserError( - "Payment Term tidak dapat diubah karena Sales Order sedang dalam proses approval atau sudah diapprove.") - - if 'payment_term_id' in vals: - for order in self: - partner = order.partner_id.parent_id or order.partner_id - customer_payment_term = partner.property_payment_term_id - - # Jika payment term yang diinput berbeda dengan payment term customer - if vals['payment_term_id'] != customer_payment_term.id: - raise UserError( - f"Payment Term berbeda pada Master Data Customer. " - f"Harap ganti ke '{customer_payment_term.name}' " - f"sesuai dengan payment term yang terdaftar pada customer." - ) - - return super(SaleOrder, self).write(vals) ->>>>>>> 46e4c80e1b530274c01cca5603f3e1be873820f6 diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3135f41c..0071bb1a 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -288,7 +288,7 @@ class StockPicking(models.Model): self.ensure_one() if not self.name or not self.origin: return False - return f"{self.name}, {self.origin}" + return f"{self.name} {self.origin}" def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" @@ -584,7 +584,6 @@ class StockPicking(models.Model): self.lalamove_phone = phone self.lalamove_status = pod.get("status") self.lalamove_delivered_at = delivered_at_dt - self.driver_arrival_date = delivered_at_dt return data raise UserError("No delivered data found in Lalamove response.") -- cgit v1.2.3 From f66710c029d678ecf580808b5aee3821c90d9598 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:38:49 +0700 Subject: Revert "Merge branch 'CR/form-merchant' into odoo-backup" This reverts commit f6f59e660af6c4229ada54f7313d68867df1ba15, reversing changes made to 68378dd2fb8d61b282a672ca0f09033d15d82283. --- indoteknik_custom/models/__init__.py | 2 - indoteknik_custom/models/res_partner.py | 184 +--------------------- indoteknik_custom/models/sale_order.py | 46 ------ indoteknik_custom/models/user_form_merchant.py | 98 ------------ indoteknik_custom/models/user_merchant_request.py | 125 --------------- 5 files changed, 1 insertion(+), 454 deletions(-) delete mode 100644 indoteknik_custom/models/user_form_merchant.py delete mode 100644 indoteknik_custom/models/user_merchant_request.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index e17f68d1..08fa9803 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -144,8 +144,6 @@ from . import stock_immediate_transfer from . import coretax_fatur from . import public_holiday from . import ir_actions_report -from . import user_form_merchant -from . import user_merchant_request from . import barcoding_product from . import sales_order_koli from . import stock_backorder_confirmation diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index ff07c94c..191a44c9 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -161,86 +161,6 @@ class ResPartner(models.Model): "this feature", tracking=3) telegram_id = fields.Char(string="Telegram") - # 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') - 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') - 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)'), - ]) - # Informasi Vendor - harga_tayang = fields.Char(string='Harga Tayang (HET)') - 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): return self.env.ref('__export__.account_payment_term_26_484409e2').id @@ -409,60 +329,6 @@ 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 - # 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) - 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['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) - 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) - # Simpan hanya field yang perlu di-update pada child vals_for_child = { 'customer_type': vals.get('customer_type'), @@ -534,55 +400,7 @@ class ResPartner(models.Model): 'dokumen_tempat_bekerja': vals.get('dokumen_tempat_bekerja'), # internal_notes - 'comment': vals.get('comment'), - - # 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'), - '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'), - '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'), + 'comment': vals.get('comment') } # Lakukan update pada semua child secara rekursif diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6028a1d0..bdf8f1eb 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -336,12 +336,6 @@ class SaleOrder(models.Model): date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' ) - ready_to_ship_status_detail = fields.Char( - string='Status Shipping Detail', - compute='_compute_ready_to_ship_status_detail' - ) - - def _compute_total_margin_excl_third_party(self): for order in self: if order.amount_untaxed == 0: @@ -2012,45 +2006,5 @@ class SaleOrder(models.Model): if any(fields in vals for fields in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): self._validate_delivery_amt() if any(field in vals for field in ["order_line", "client_order_ref"]): -<<<<<<< HEAD self._calculate_etrts_date() return res -======= - self._calculate_etrts_date() - return res - - # @api.depends('commitment_date') - def _compute_ready_to_ship_status_detail(self): - for order in self: - eta = order.commitment_date - - match_lines = self.env['purchase.order.sales.match'].search([ - ('sale_id', '=', order.id) - ]) - - if match_lines: - for match in match_lines: - po = match.purchase_order_id - product = match.product_id - - po_line = self.env['purchase.order.line'].search([ - ('order_id', '=', po.id), - ('product_id', '=', product.id) - ], limit=1) - - stock_move = self.env['stock.move'].search([ - ('purchase_line_id', '=', po_line.id) - ], limit=1) - picking_in = stock_move.picking_id - - result_date = picking_in.date_done if picking_in else None - if result_date: - status = "Early" if result_date < eta else "Delay" - result_date_str = result_date.strftime('%m/%d/%Y') - eta_str = eta.strftime('%m/%d/%Y') - order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}" - else: - order.ready_to_ship_status_detail = "On Track" - else: - order.ready_to_ship_status_detail = 'On Track' ->>>>>>> CR/form-merchant diff --git a/indoteknik_custom/models/user_form_merchant.py b/indoteknik_custom/models/user_form_merchant.py deleted file mode 100644 index a804e93f..00000000 --- a/indoteknik_custom/models/user_form_merchant.py +++ /dev/null @@ -1,98 +0,0 @@ -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') - # informasi peruhsaan - 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') - 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') - bisnis_type = fields.Selection([ - ('1', 'PT'), - ('2', 'CV'), - ('3', 'Perorangan'), - ]) - website = 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)'), - ]) - description = fields.Text(string='Deskripsi') - - # imformasi Vendor - harga_tayang = fields.Char(string='Harga Tayang (HET)') - 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') - 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.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 deleted file mode 100644 index dd571cdc..00000000 --- a/indoteknik_custom/models/user_merchant_request.py +++ /dev/null @@ -1,125 +0,0 @@ -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: - # 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 - 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.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.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 - 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 -- cgit v1.2.3 From d27999cacee59a115ae3c6c46542c2e20cfe176b Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 09:42:03 +0700 Subject: bf cant create invoice dp cause of custom method action assign stock picking --- indoteknik_custom/models/stock_picking.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0071bb1a..0fcb7ca1 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1012,15 +1012,15 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - if self.sale_id.hold_outgoing and self.location_id.id == 57 and self.location_dest_id.id == 60: - print(1) - else: - res = super(StockPicking, self).action_assign() + res = super(StockPicking, self).action_assign() + for move in self: + # if not move.sale_id.hold_outgoing and move.location_id.id != 57 and move.location_dest_id.id != 60: + # TODO cant skip hold outgoing cause of not singleton method current_time = datetime.datetime.utcnow() - self.real_shipping_id = self.sale_id.real_shipping_id - self.date_availability = current_time + move.real_shipping_id = move.sale_id.real_shipping_id + move.date_availability = current_time # self.check_state_reserve() - return res + return res def ask_approval(self): if self.env.user.is_accounting: -- cgit v1.2.3 From 019bba2a3ff2ed08f53200df02c0638ddabbbe22 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 11:16:30 +0700 Subject: bf outgoing with hold qty and change formula of reordering rule --- indoteknik_custom/models/automatic_purchase.py | 2 +- indoteknik_custom/models/product_template.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index 4feec307..1d7c5e31 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -486,7 +486,7 @@ class AutomaticPurchase(models.Model): # _logger.info('test %s' % point.product_id.name) if point.product_id.qty_available_bandengan > point.product_min_qty: continue - qty_purchase = point.product_max_qty - point.product_id.qty_available_bandengan + qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - point.product_id.qty_onhand_bandengan po_line = self.env['purchase.order.line'].search([('product_id', '=', point.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1) if self.vendor_id: diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 34b8fd2c..c62ca63e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -1116,7 +1116,8 @@ class ProductProduct(models.Model): domain=[ ('product_id', '=', product.id), ('location_id', 'in', [57, 83]), - ('mo_id', '=', False) + ('mo_id', '=', False), + ('hold_outgoing', '=', False) ], fields=['qty_need'], groupby=[] @@ -1359,6 +1360,7 @@ class OutstandingMove(models.Model): location_id = fields.Many2one('stock.location', string='Location', help='Lokasi asal') location_dest_id = fields.Many2one('stock.location', string='Location To', help='Lokasi tujuan') mo_id = fields.Many2one('mrp.production', string='Manufacturing Order') + hold_outgoing = fields.Boolean(string='Hold Outgoing') def init(self): # where clause 'state in' follow the origin of outgoing and incoming odoo @@ -1368,8 +1370,11 @@ class OutstandingMove(models.Model): select sm.id, sm.reference, sm.product_id, sm.product_uom_qty as qty_need, sm.location_id, sm.location_dest_id, - sm.raw_material_production_id as mo_id + sm.raw_material_production_id as mo_id, + so.hold_outgoing from stock_move sm + left join procurement_group pg on pg.id = sm.group_id + left join sale_order so on so.id = pg.sale_id where 1=1 and sm.state in( 'waiting', -- cgit v1.2.3 From 87e533be1993fd6730ead008e6fdd676d48c2bb7 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 23 May 2025 14:15:10 +0700 Subject: fix bug website checkout purchase_tax_id --- indoteknik_custom/models/sale_order_line.py | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 049bee2d..753e3e29 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -255,32 +255,33 @@ class SaleOrderLine(models.Model): def _get_valid_purchase_price(self, purchase_price): current_time = datetime.now() delta_time = current_time - timedelta(days=365) + default_timestamp = datetime(1970, 1, 1, 0, 0, 0) # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') price = 0 - taxes = '' + taxes = 24 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 + #if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 + vendor_id = purchase_price.vendor_id.id + if delta_time > human_last_update: + price = 0 + taxes = 24 + 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 or 24 vendor_id = purchase_price.vendor_id.id - if delta_time > human_last_update: + if delta_time > system_last_update: price = 0 - taxes = '' + taxes = 24 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 -- cgit v1.2.3 From 8f5b6e060c8dd08f01476ac5469faa6fce706c55 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 14:16:57 +0700 Subject: bf cant get purchase price cause of null in purchase tax --- indoteknik_custom/models/sale_order_line.py | 35 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 049bee2d..3c4965e5 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -255,33 +255,34 @@ class SaleOrderLine(models.Model): def _get_valid_purchase_price(self, purchase_price): current_time = datetime.now() delta_time = current_time - timedelta(days=365) + default_timestamp = datetime(1970, 1, 1, 0, 0, 0) # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') price = 0 - taxes = '' + taxes = 24 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 + + # if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 + vendor_id = purchase_price.vendor_id.id + if delta_time > human_last_update: + price = 0 + taxes = 24 + 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 or 24 vendor_id = purchase_price.vendor_id.id - if delta_time > human_last_update: + if delta_time > system_last_update: price = 0 - taxes = '' + taxes = 24 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 @api.onchange('product_id') -- cgit v1.2.3 From 936c6cf2c1aea0e2b470d20d6fd41a10380bcac0 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 14:18:15 +0700 Subject: bf cant get purchase price cause of null in tax --- indoteknik_custom/models/sale_order_line.py | 35 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 049bee2d..3c4965e5 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -255,33 +255,34 @@ class SaleOrderLine(models.Model): def _get_valid_purchase_price(self, purchase_price): current_time = datetime.now() delta_time = current_time - timedelta(days=365) + default_timestamp = datetime(1970, 1, 1, 0, 0, 0) # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') price = 0 - taxes = '' + taxes = 24 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 + + # if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 + vendor_id = purchase_price.vendor_id.id + if delta_time > human_last_update: + price = 0 + taxes = 24 + 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 or 24 vendor_id = purchase_price.vendor_id.id - if delta_time > human_last_update: + if delta_time > system_last_update: price = 0 - taxes = '' + taxes = 24 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 @api.onchange('product_id') -- cgit v1.2.3 From 76d5328565d394c5e78c56c7c2fc37e5470022ce Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 23 May 2025 15:06:29 +0700 Subject: refactor get valid purchase price cause of null in tax purchase --- indoteknik_custom/models/automatic_purchase.py | 14 +++++------ indoteknik_custom/models/mrp_production.py | 32 +++++++++++++------------- indoteknik_custom/models/requisition.py | 14 +++++------ 3 files changed, 30 insertions(+), 30 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index b66121e1..c5cc686a 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -589,18 +589,18 @@ class AutomaticPurchaseLine(models.Model): def _get_valid_purchase_price(self, purchase_price): price = 0 - taxes = '' + taxes = 24 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 + #if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 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 + #if purchase_price.taxes_system_id.type_tax_use == 'purchase': + price = purchase_price.system_price + taxes = purchase_price.taxes_system_id.id or 24 return price, taxes diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 58c2512c..14821f27 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -180,29 +180,29 @@ class MrpProduction(models.Model): # delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S') price = 0 - taxes = '' + taxes = 24 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 + #if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 + 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 or 24 vendor_id = purchase_price.vendor_id.id - if delta_time > human_last_update: + if delta_time > system_last_update: price = 0 - taxes = '' + taxes = 24 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 diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py index 1d350929..74236850 100644 --- a/indoteknik_custom/models/requisition.py +++ b/indoteknik_custom/models/requisition.py @@ -299,18 +299,18 @@ class RequisitionLine(models.Model): def _get_valid_purchase_price(self, purchase_price): price = 0 - taxes = '' + taxes = 24 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 + #if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id or 24 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 + #if purchase_price.taxes_system_id.type_tax_use == 'purchase': + price = purchase_price.system_price + taxes = purchase_price.taxes_system_id.id or 24 return price, taxes -- cgit v1.2.3 From 64c86581b469e962789ea6a7ea45c17ed020fe9a Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Sat, 24 May 2025 11:23:38 +0700 Subject: add putra to role sales manager in user pengajuan tempo request --- indoteknik_custom/models/user_pengajuan_tempo_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 565b0315..aae09cc4 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -365,13 +365,13 @@ class UserPengajuanTempoRequest(models.Model): @api.onchange('tempo_duration') def _tempo_duration_change(self): for tempo in self: - if tempo.env.user.id not in (7, 688, 28, 377, 12182): + if tempo.env.user.id not in (7, 688, 28, 377, 12182, 375): raise UserError("Durasi tempo hanya bisa di ubah oleh Sales Manager atau Direktur") @api.onchange('tempo_limit') def _onchange_tempo_limit(self): for tempo in self: - if tempo.env.user.id not in (7, 688, 28, 377, 12182): + if tempo.env.user.id not in (7, 688, 28, 377, 12182, 375): raise UserError("Limit tempo hanya bisa diubah oleh Sales Manager atau Direktur") def button_approve(self): for tempo in self: @@ -381,7 +381,7 @@ class UserPengajuanTempoRequest(models.Model): if tempo.env.user.id in (688, 28, 7): raise UserError("Pengajuan tempo harus di approve oleh sales manager terlebih dahulu") else: - if tempo.env.user.id not in (377, 12182): + if tempo.env.user.id not in (377, 12182, 375): # if tempo.env.user.id != 12182: raise UserError("Pengajuan tempo hanya bisa di approve oleh sales manager") else: -- cgit v1.2.3 From 8fefc2e71c5e8a9f040f2fe6c9310b20cd88db3f Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 26 May 2025 09:00:23 +0700 Subject: fix approval invoice date --- indoteknik_custom/models/approval_invoice_date.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/approval_invoice_date.py b/indoteknik_custom/models/approval_invoice_date.py index b58d55d4..e1bc4c9b 100644 --- a/indoteknik_custom/models/approval_invoice_date.py +++ b/indoteknik_custom/models/approval_invoice_date.py @@ -32,6 +32,7 @@ class ApprovalInvoiceDate(models.Model): if not self.env.user.is_accounting: raise UserError("Hanya Accounting Yang Bisa Approve") self.move_id.invoice_date = self.date_doc_do.date() + self.picking_id.date_doc_kirim = self.date_doc_do self.state = 'done' self.approve_date = datetime.utcnow() self.approve_by = self.env.user.id -- cgit v1.2.3