from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError from datetime import datetime, timedelta from odoo.http import request import re import requests import logging _logger = logging.getLogger(__name__) class GroupPartner(models.Model): _name = 'group.partner' name = fields.Char(string='Name') class ResPartner(models.Model): _inherit = 'res.partner' property_account_payable_id = fields.Many2one('account.account', company_dependent=True, string="Account Payable", domain="[('internal_type', '=', 'payable'), ('deprecated', '=', False), ('company_id', '=', current_company_id)]", help="This account will be used instead of the default one as the payable account for the current partner", default=438) property_account_receivable_id = fields.Many2one('account.account', company_dependent=True, string="Account Receivable", domain="[('internal_type', '=', 'receivable'), ('deprecated', '=', False), ('company_id', '=', current_company_id)]", help="This account will be used instead of the default one as the receivable account for the current partner", default=395) # Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers") reminder_invoices = fields.Boolean( string='Reminder Invoice?', help='Centang jika kontak ini harus menerima email pengingat invoice.', default=False, tracking=True ) dont_send_reminder_inv_parent = fields.Boolean( string='Dont Send Reminder Invoices Parent?', help='Centang jika kontak utama tidak perlu menerima sent Reminder Invoices Otomatis', default=False, tracking=True ) dont_send_reminder_inv_all = fields.Boolean( string='Dont Send Reminder Invoices to All?', help='Centang jika semua kontak utama dan child tidak menerima sent Reminder Invoices', default=False, tracking=True ) # informasi perusahaan name_tempo = fields.Many2one('res.partner', string='Nama Perusahaan',tracking=True) industry_id_tempo = fields.Many2one('res.partner.industry', 'Customer Industry', readonly=True) street_tempo = fields.Char(string="Alamat Perusahaan") state_id_tempo = fields.Many2one('res.country.state', string='State') city_id_tempo = fields.Many2one('vit.kota', string='City') zip_tempo = fields.Char(string="Zip") mobile_tempo = fields.Char(string="No. Telfon Perusahaan") bank_name_tempo = fields.Char(string="Nama Bank") account_name_tempo = fields.Char(string="Nama Rekening") account_number_tempo = fields.Char(string="Nomor Rekening Bank") website_tempo = fields.Char(string='Website') portal = fields.Boolean(string='Portal Website') estimasi_tempo = fields.Char(string='Estimasi Pembelian Pertahun') tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') tempo_limit = fields.Char(string='Limit Tempo') minimum_amount = fields.Float( string="Minimum Order", help="Jika total belanja kurang dari ini, maka payment term akan otomatis menjadi CBD." ) minimum_amount_tax = fields.Float( string="Minimum Amount Tax", help="Jika total belanja kurang dari ini, maka tax akan otomatis menjadi 0%." ) category_produk_ids = fields.Many2many('product.public.category', string='Kategori Produk yang Digunakan', domain=lambda self: self._get_default_category_domain()) @api.model def _get_default_category_domain(self): return [('parent_id', '=', False)] # Kontak Perusahaan direktur_name = fields.Char(string='Nama Lengkap Direktur') direktur_mobile = fields.Char(string='No. Telpon Direktur') direktur_email = fields.Char(string='Email Direktur') purchasing_name = fields.Char(string='Nama Purchasing') purchasing_mobile = fields.Char(string='No. Telpon Purchasing') purchasing_email = fields.Char(string='Email Purchasing') finance_name = fields.Char(string='Nama Finance') finance_mobile = fields.Char(string='No. Telpon Finance') finance_email = fields.Char(string='Email Finance') # Pengiriman pic_name = fields.Char(string='Nama PIC Penerimaan Barang') pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang') street_pengiriman = fields.Char(string="Alamat Perusahaan") state_id_pengiriman = fields.Many2one('res.country.state', string='State') city_id_pengiriman = fields.Many2one('vit.kota', string='City') district_id_pengiriman = fields.Many2one('vit.kecamatan', string='Kecamatan') subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan') zip_pengiriman = fields.Char(string="Zip") invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice') invoice_pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice') street_invoice = fields.Char(string="Alamat Perusahaan") state_id_invoice = fields.Many2one('res.country.state', string='State') city_id_invoice = fields.Many2one('vit.kota', string='City') district_id_invoice = fields.Many2one('vit.kecamatan', string='Kecamatan') subDistrict_id_invoice = fields.Many2one('vit.kelurahan', string='Kelurahan') zip_invoice = fields.Char(string="Zip") tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice') jadwal_bayar = fields.Char(string='Jadwal Pembayaran') dokumen_prosedur = fields.Many2one('ir.attachment', string="Dokumen Pengiriman", tracking=3, readonly=True) dokumen_pengiriman = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang') dokumen_pengiriman_input = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang') dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice') # Dokumen dokumen_npwp = fields.Many2one('ir.attachment', string="NPWP Perusahaan", tracking=3, readonly=True) dokumen_sppkp = fields.Many2one('ir.attachment', string="SPPKP Perusahaan", tracking=3, readonly=True) dokumen_nib = fields.Many2one('ir.attachment', string="NIB (SIUP/TDP/SKDP)", tracking=3, readonly=True,) dokumen_siup = fields.Many2one('ir.attachment', string="SIUP Perusahaan", tracking=3, readonly=True) dokumen_tdp = fields.Many2one('ir.attachment', string="TDP Perusahaan", tracking=3, readonly=True) dokumen_skdp = fields.Many2one('ir.attachment', string="SKDP Perusahaan",tracking=True, readonly=True) dokumen_skt = fields.Many2one('ir.attachment', string="SKT Perusahaan", tracking=True, readonly=True) dokumen_akta_perubahan = fields.Many2one('ir.attachment', string="Akta Perubahan", tracking=3, readonly=True) dokumen_ktp_dirut = fields.Many2one('ir.attachment', string="KTP Dirut/Direktur", tracking=3, readonly=True) dokumen_akta_pendirian = fields.Many2one('ir.attachment', string="Akta Pendirian", tracking=3, readonly=True) dokumen_laporan_keuangan = fields.Many2one('ir.attachment', string="Laporan Keuangan", tracking=3, readonly=True) dokumen_foto_kantor = fields.Many2one('ir.attachment', string=" Foto Kantor (Tampak Depan)", tracking=3, readonly=True) dokumen_tempat_bekerja = fields.Many2one('ir.attachment', string="Tempat Bekerja", tracking=3, readonly=True) reference_number = fields.Char(string="Reference Number") company_type_id = fields.Many2one('res.partner.company_type', string='Company Type') custom_pricelist_id = fields.Many2one('product.pricelist', string='Price Matrix') group_partner_id = fields.Many2one('group.partner', string='Group Partner') customer_type = fields.Selection([ ('pkp', 'PKP'), ('nonpkp', 'Non PKP') ]) sppkp = fields.Char(string="SPPKP", tracking=True) npwp = fields.Char(string="NPWP", tracking=True) nitku = fields.Char(string="NITKU", tracking=True) counter = fields.Integer(string="Counter", default=0) leadtime = fields.Integer(string="Leadtime", default=0) digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak") is_not_potential = fields.Boolean(string='Not Potential') pakta_integritas = fields.Boolean(string='Pakta Integritas') use_so_approval = fields.Boolean(string='Use SO Approval') use_only_ready_stock = fields.Boolean(string='Use Only Ready Stock') web_role = fields.Selection([ ('manager', 'Manager'), ('director', 'Director'), ('procurement', 'Procurement'), ], string='Web Role') site_id = fields.Many2one('res.partner.site', string='Site') main_parent_id = fields.Many2one('res.partner', string='Main Parent', compute='_compute_main_parent_id') pareto_status = fields.Selection([ ('PR', 'Pareto Repeating'), ('PPR', 'Potensi Pareto Repeating'), ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) email_finance = fields.Char(string='Email Finance Vendor') email_sales = fields.Char(string='Email Sales Vendor') user_payment_terms_sales = fields.Many2one('res.users', string='Users Update Payment Terms') date_payment_terms_sales = fields.Datetime(string='Date Update Payment Terms') user_payment_terms_purchase = fields.Many2one('res.users', string='Users Update Payment Terms') date_payment_terms_purchase = fields.Datetime(string='Date Update Payment Terms') longtitude = fields.Char(string='Longtitude') latitude = fields.Char(string='Latitude') map_view = fields.Char(string='Map') address_map = fields.Char(string='Address Map', help='Alamat ini diisi otomatis berdasarkan koordinat pin pada peta. Silakan koreksi dan ubah jika terdapat ketidaksesuaian', tracking=3) company_type = fields.Selection(string='Company Type', selection=[('person', 'Individual'), ('company', 'Company')], compute='_compute_company_type', inverse='_write_company_type', tracking=3) warning_stage = fields.Float(string='Warning Amount', help="A warning message will appear once the " "selected customer is crossed warning " "amount. Set its value to 0.00 to" " disable this feature", tracking=3) blocking_stage = fields.Float(string='Blocking Amount', help="Cannot make sales once the selected " "customer is crossed blocking amount." "Set its value to 0.00 to disable " "this feature", tracking=3) telegram_id = fields.Char(string="Telegram") avg_aging= fields.Float(string='Average Aging') payment_difficulty = fields.Selection([('bermasalah', 'Bermasalah'),('sulit', 'Sulit'),('agak_sulit', 'Agak Sulit'),('normal', 'Normal')], string='Payment Difficulty', tracking=3) payment_history_url = fields.Text(string='Payment History URL') is_cbd_locked = fields.Boolean("Locked to CBD?", default=False, tracking=True, help="Jika dicentang, maka partner ini terkunci pada payment term CBD karena memiliki invoice yang sudah jatuh tempo lebih dari 30 hari.") cbd_lock_date = fields.Datetime("CBD Lock Date", tracking=True, help="Tanggal ketika partner ini dikunci pada payment term CBD.") chat_id_telegram = fields.Char(string='ChatId Telegram') @api.model def _default_payment_term(self): return self.env.ref('__export__.account_payment_term_26_484409e2').id property_payment_term_id = fields.Many2one( 'account.payment.term', string='Payment Terms', default=_default_payment_term, tracking=3 ) previous_payment_term_id = fields.Many2one( 'account.payment.term', string='Previous Payment Term' ) property_product_pricelist = fields.Many2one( tracking=True ) @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id", "kecamatan_id") def _alamat_lengkap_text(self): for partner in self: if partner.company_type == 'person' and not partner.parent_id: partner.alamat_lengkap_text = partner.street # if partner.company_type == 'person' and partner.parent_id: # partner.alamat_lengkap_text = partner.parent_id.alamat_lengkap_text alamat_lengkap_text = fields.Text(string="Alamat Lengkap", required=False, tracking=3) # def write(self, vals): # res = super(ResPartner, self).write(vals) # use_pin = bool(self.env.context.get('use_pin')) # for rec in self: # if use_pin and 'latitude' in vals or 'longtitude' in vals: # # rec._update_address_from_coords() # rec.with_context(overwrite_street_from_pin=False)._update_address_from_coords() # # Sinkronisasi payment_difficulty ke semua anak jika partner ini adalah parent # if not rec.parent_id and 'payment_difficulty' in vals: # rec.child_ids.write({'payment_difficulty': vals['payment_difficulty']}) # # # if 'property_payment_term_id' in vals: # # if not self.env.user.is_accounting and vals['property_payment_term_id'] != 26: # # raise UserError('Hanya Finance Accounting yang dapat merubah payment term') # # # group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id # # users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) # # if self.env.user.id not in users_in_group.mapped('id'): # # raise UserError('You name it') # def write(self, vals): res = super(ResPartner, self).write(vals) use_pin = bool(self.env.context.get('use_pin')) for rec in self: # Hanya jalan kalau explicit use_pin DAN ada lat/lng di payload if use_pin and (('latitude' in vals) or ('longtitude' in vals)): # optional guard: pastikan bukan kosong/0 lat = rec.latitude lng = rec.longtitude if lat and lng and str(lat) not in ('0', '0.0') and str(lng) not in ('0', '0.0'): rec.with_context(overwrite_street_from_pin=False)._update_address_from_coords() # Sinkronisasi payment_difficulty ke anak if not rec.parent_id and 'payment_difficulty' in vals: rec.child_ids.write({'payment_difficulty': vals['payment_difficulty']}) return res # @api.model # def create(self, vals): # records = super().create(vals) # use_pin = bool(self.env.context.get('use_pin')) # for rec in records: # if use_pin and vals.get('latitude') and vals.get('longtitude'): # rec._update_address_from_coords() # if rec.parent_id and not vals.get('payment_difficulty'): # rec.payment_difficulty = rec.parent_id.payment_difficulty # return records @api.model def create(self, vals): records = super().create(vals) use_pin = bool(self.env.context.get('use_pin')) for rec in records: if use_pin and vals.get('latitude') and vals.get('longtitude'): rec.with_context(overwrite_street_from_pin=False)._update_address_from_coords() if rec.parent_id and not vals.get('payment_difficulty'): rec.payment_difficulty = rec.parent_id.payment_difficulty return records @api.constrains('email') def _check_duplicate_name(self): for record in self: if record.name: existing_partner = self.env['res.partner'].search([ ('id', '!=', record.id), ('name', '=', record.name), ('email', '=', record.email) ], limit=1) if existing_partner and not record.parent_id: raise ValidationError(f"Nama '{record.name}' dengan email '{record.email}' sudah digunakan oleh partner lain!") @api.constrains('npwp') def _check_npwp(self): for record in self: npwp = record.npwp.strip() if record.npwp else '' if not npwp or npwp == '0' or npwp == '00.000.000.0-000.000': continue elif len(npwp) == 15 and npwp.isdigit(): formatted = f"{npwp[0:2]}.{npwp[2:5]}.{npwp[5:8]}.{npwp[8]}-{npwp[9:12]}.{npwp[12:15]}" record.npwp = formatted elif len(npwp) == 20: pattern_15_digit = r'^\d{2}\.\d{3}\.\d{3}\.\d{1}-\d{3}\.\d{3}$' if not re.match(pattern_15_digit, npwp): raise ValidationError( "Format NPWP 15 digit yang dimasukkan salah. " "Pastikan format yang benar adalah: 99.999.999.9-999.999" ) elif len(npwp) == 16: pattern_16_digit = r'^\d{16}$' if not re.match(pattern_16_digit, npwp): raise ValidationError( "Format NPWP 16 digit yang dimasukkan salah. " "Format yang benar adalah 16 digit angka tanpa titik atau tanda hubung." ) else: raise ValidationError( "Digit NPWP yang dimasukkan tidak sesuai. " "Pastikan NPWP memiliki 15 digit dengan format tertentu " "(99.999.999.9-999.999) atau 16 digit tanpa tanda hubung." ) # def write(self, vals): # # Fungsi rekursif untuk meng-update semua child, termasuk child dari child # def update_children_recursively(partner, vals_for_child): # # Lakukan update pada partner saat ini hanya dengan field yang diizinkan # partner.write(vals_for_child) # # # Untuk setiap child dari partner ini, update juga child-nya # for child in partner.child_ids: # update_children_recursively(child, vals_for_child) # # # Jika self tidak memiliki parent_id, artinya self adalah parent # if not self.parent_id: # # Ambil semua child dari parent ini # children = self.child_ids # # # Perbarui vals dengan nilai dari parent jika tidak ada dalam vals # vals['customer_type'] = vals.get('customer_type', self.customer_type) # vals['nama_wajib_pajak'] = vals.get('nama_wajib_pajak', self.nama_wajib_pajak) # vals['npwp'] = vals.get('npwp', self.npwp) # vals['sppkp'] = vals.get('sppkp', self.sppkp) # vals['alamat_lengkap_text'] = vals.get('alamat_lengkap_text', self.alamat_lengkap_text) # vals['industry_id'] = vals.get('industry_id', self.industry_id.id if self.industry_id else None) # vals['company_type_id'] = vals.get('company_type_id', self.company_type_id.id if self.company_type_id else None) # # # Referensi # vals['supplier_ids'] = vals.get('supplier_ids', self.supplier_ids) # # # informasi perusahaan # vals['name_tempo'] = vals.get('name_tempo', self.name_tempo) # vals['industry_id_tempo'] = vals.get('industry_id_tempo', self.industry_id_tempo) # vals['street_tempo'] = vals.get('street_tempo', self.street_tempo) # vals['state_id_tempo'] = vals.get('state_id_tempo', self.state_id_tempo) # vals['city_id_tempo'] = vals.get('city_id_tempo', self.city_id_tempo) # vals['zip_tempo'] = vals.get('zip_tempo', self.zip_tempo) # vals['bank_name_tempo'] = vals.get('bank_name_tempo', self.bank_name_tempo) # vals['account_name_tempo'] = vals.get('account_name_tempo', self.account_name_tempo) # vals['account_number_tempo'] = vals.get('account_number_tempo', self.account_number_tempo) # vals['website_tempo'] = vals.get('website_tempo', self.website_tempo) # vals['portal'] = vals.get('portal', self.portal) # vals['estimasi_tempo'] = vals.get('estimasi_tempo', self.estimasi_tempo) # vals['tempo_duration'] = vals.get('tempo_duration', self.tempo_duration) # vals['tempo_limit'] = vals.get('tempo_limit', self.tempo_limit) # vals['category_produk_ids'] = vals.get('category_produk_ids', self.category_produk_ids) # # # Kontak Perusahaan # vals['direktur_name'] = vals.get('direktur_name', self.direktur_name) # vals['direktur_mobile'] = vals.get('direktur_mobile', self.direktur_mobile) # vals['direktur_email'] = vals.get('direktur_email', self.direktur_email) # vals['purchasing_name'] = vals.get('purchasing_name', self.purchasing_name) # vals['purchasing_mobile'] = vals.get('purchasing_mobile', self.purchasing_mobile) # vals['purchasing_email'] = vals.get('purchasing_email', self.purchasing_email) # vals['finance_name'] = vals.get('finance_name', self.finance_name) # vals['finance_mobile'] = vals.get('finance_mobile', self.finance_mobile) # vals['finance_email'] = vals.get('finance_email', self.finance_email) # # # Pengiriman # vals['pic_name'] = vals.get('pic_name', self.pic_name) # vals['pic_mobile'] = vals.get('pic_mobile', self.pic_mobile) # vals['street_pengiriman'] = vals.get('street_pengiriman', self.street_pengiriman) # vals['state_id_pengiriman'] = vals.get('state_id_pengiriman', self.state_id_pengiriman) # vals['city_id_pengiriman'] = vals.get('city_id_pengiriman', self.city_id_pengiriman) # vals['district_id_pengiriman'] = vals.get('district_id_pengiriman', self.district_id_pengiriman) # vals['subDistrict_id_pengiriman'] = vals.get('subDistrict_id_pengiriman', self.subDistrict_id_pengiriman) # vals['zip_pengiriman'] = vals.get('zip_pengiriman', self.zip_pengiriman) # vals['invoice_pic'] = vals.get('invoice_pic', self.invoice_pic) # vals['invoice_pic_mobile'] = vals.get('invoice_pic_mobile', self.invoice_pic_mobile) # vals['street_invoice'] = vals.get('street_invoice', self.street_invoice) # vals['state_id_invoice'] = vals.get('state_id_invoice', self.state_id_invoice) # vals['city_id_invoice'] = vals.get('city_id_invoice', self.city_id_invoice) # vals['district_id_invoice'] = vals.get('district_id_invoice', self.district_id_invoice) # vals['subDistrict_id_invoice'] = vals.get('subDistrict_id_invoice', self.subDistrict_id_invoice) # vals['zip_invoice'] = vals.get('zip_invoice', self.zip_invoice) # vals['tukar_invoice'] = vals.get('tukar_invoice', self.tukar_invoice) # vals['jadwal_bayar'] = vals.get('jadwal_bayar', self.jadwal_bayar) # vals['dokumen_prosedur'] = vals.get('dokumen_prosedur', self.dokumen_prosedur) # vals['dokumen_pengiriman'] = vals.get('dokumen_pengiriman', self.dokumen_pengiriman) # vals['dokumen_pengiriman_input'] = vals.get('dokumen_pengiriman_input', self.dokumen_pengiriman_input) # vals['dokumen_invoice'] = vals.get('dokumen_invoice', self.dokumen_invoice) # # # Dokumen # vals['dokumen_npwp'] = vals.get('dokumen_npwp', self.dokumen_npwp) # vals['dokumen_sppkp'] = vals.get('dokumen_sppkp', self.dokumen_sppkp) # vals['dokumen_nib'] = vals.get('dokumen_nib', self.dokumen_nib) # vals['dokumen_siup'] = vals.get('dokumen_siup', self.dokumen_siup) # vals['dokumen_tdp'] = vals.get('dokumen_tdp', self.dokumen_tdp) # vals['dokumen_skdp'] = vals.get('dokumen_skdp', self.dokumen_skdp) # vals['dokumen_skt'] = vals.get('dokumen_skt', self.dokumen_skt) # vals['dokumen_akta_perubahan'] = vals.get('dokumen_akta_perubahan', self.dokumen_akta_perubahan) # vals['dokumen_ktp_dirut'] = vals.get('dokumen_ktp_dirut', self.dokumen_ktp_dirut) # vals['dokumen_akta_pendirian'] = vals.get('dokumen_akta_pendirian', self.dokumen_akta_pendirian) # vals['dokumen_laporan_keuangan'] = vals.get('dokumen_laporan_keuangan', self.dokumen_laporan_keuangan) # vals['dokumen_foto_kantor'] = vals.get('dokumen_foto_kantor', self.dokumen_foto_kantor) # vals['dokumen_tempat_bekerja'] = vals.get('dokumen_tempat_bekerja', self.dokumen_tempat_bekerja) # # # Simpan hanya field yang perlu di-update pada child # vals_for_child = { # 'customer_type': vals.get('customer_type'), # 'nama_wajib_pajak': vals.get('nama_wajib_pajak'), # 'npwp': vals.get('npwp'), # 'sppkp': vals.get('sppkp'), # 'alamat_lengkap_text': vals.get('alamat_lengkap_text'), # 'industry_id': vals.get('industry_id'), # 'company_type_id': vals.get('company_type_id'), # 'supplier_ids': vals.get('supplier_ids'), # 'name_tempo': vals.get('name_tempo'), # 'industry_id_tempo': vals.get('industry_id_tempo'), # 'street_tempo': vals.get('street_tempo'), # 'state_id_tempo': vals.get('state_id_tempo'), # 'city_id_tempo': vals.get('city_id_tempo'), # 'zip_tempo': vals.get('zip_tempo'), # 'bank_name_tempo': vals.get('bank_name_tempo'), # 'account_name_tempo': vals.get('account_name_tempo'), # 'account_number_tempo': vals.get('account_number_tempo'), # 'website_tempo': vals.get('website_tempo'), # 'portal': vals.get('portal'), # 'estimasi_tempo': vals.get('estimasi_tempo'), # 'tempo_duration': vals.get('tempo_duration'), # 'tempo_limit': vals.get('tempo_limit'), # 'category_produk_ids': vals.get('category_produk_ids'), # 'direktur_name': vals.get('direktur_name'), # 'direktur_mobile': vals.get('direktur_mobile'), # 'direktur_email': vals.get('direktur_email'), # 'purchasing_name': vals.get('purchasing_name'), # 'purchasing_mobile': vals.get('purchasing_mobile'), # 'purchasing_email': vals.get('purchasing_email'), # 'finance_name': vals.get('finance_name'), # 'finance_mobile': vals.get('finance_mobile'), # 'finance_email': vals.get('finance_email'), # 'pic_name': vals.get('pic_name'), # 'pic_mobile': vals.get('pic_mobile'), # 'street_pengiriman': vals.get('street_pengiriman'), # 'state_id_pengiriman': vals.get('state_id_pengiriman'), # 'city_id_pengiriman': vals.get('city_id_pengiriman'), # 'district_id_pengiriman': vals.get('district_id_pengiriman'), # 'subDistrict_id_pengiriman': vals.get('subDistrict_id_pengiriman'), # 'zip_pengiriman': vals.get('zip_pengiriman'), # 'invoice_pic': vals.get('invoice_pic'), # 'invoice_pic_mobile': vals.get('invoice_pic_mobile'), # 'street_invoice': vals.get('street_invoice'), # 'state_id_invoice': vals.get('state_id_invoice'), # 'city_id_invoice': vals.get('city_id_invoice'), # 'district_id_invoice': vals.get('district_id_invoice'), # 'subDistrict_id_invoice': vals.get('subDistrict_id_invoice'), # 'zip_invoice': vals.get('zip_invoice'), # 'tukar_invoice': vals.get('tukar_invoice'), # 'jadwal_bayar': vals.get('jadwal_bayar'), # 'dokumen_prosedur': vals.get('dokumen_prosedur'), # 'dokumen_pengiriman': vals.get('dokumen_pengiriman'), # 'dokumen_pengiriman_input': vals.get('dokumen_pengiriman_input'), # 'dokumen_invoice': vals.get('dokumen_invoice'), # 'dokumen_npwp': vals.get('dokumen_npwp'), # 'dokumen_sppkp': vals.get('dokumen_sppkp'), # 'dokumen_nib': vals.get('dokumen_nib'), # 'dokumen_siup': vals.get('dokumen_siup'), # 'dokumen_tdp': vals.get('dokumen_tdp'), # 'dokumen_skdp': vals.get('dokumen_skdp'), # 'dokumen_skt': vals.get('dokumen_skt'), # 'dokumen_akta_perubahan': vals.get('dokumen_akta_perubahan'), # 'dokumen_ktp_dirut': vals.get('dokumen_ktp_dirut'), # 'dokumen_akta_pendirian': vals.get('dokumen_akta_pendirian'), # 'dokumen_laporan_keuangan': vals.get('dokumen_laporan_keuangan'), # 'dokumen_foto_kantor': vals.get('dokumen_foto_kantor'), # 'dokumen_tempat_bekerja': vals.get('dokumen_tempat_bekerja'), # # # internal_notes # 'comment': vals.get('comment') # } # # # Lakukan update pada semua child secara rekursif # for child in children: # update_children_recursively(child, vals_for_child) # # # Lakukan write untuk parent dengan vals asli # res = super(ResPartner, self).write(vals) # # return res # if self.company_type == 'person' and not partner.parent_id: # if self.parent_id: # parent = self.parent_id # vals['industry_id'] = parent.industry_id.id # vals['company_type_id'] = parent.company_type_id.id # # res = super(ResPartner, self).write(vals) # return res @api.depends('company_type', 'parent_id', 'npwp', 'sppkp', 'nama_wajib_pajak','alamat_lengkap_text', 'industry_id', 'company_type_id') def _related_fields(self): for partner in self: if partner.company_type == 'person' and partner.parent_id: partner.customer_type = partner.parent_id.customer_type partner.npwp = partner.parent_id.npwp partner.sppkp = partner.parent_id.sppkp partner.nama_wajib_pajak = partner.parent_id.nama_wajib_pajak partner.alamat_lengkap_text = partner.parent_id.alamat_lengkap_text partner.industry_id = partner.parent_id.industry_id.id partner.company_type_id = partner.parent_id.company_type_id.id @api.constrains('property_payment_term_id') def updated_by_payment_term(self): for rec in self: rec.user_payment_terms_sales = self.env.user.id rec.date_payment_terms_sales = datetime.utcnow() @api.constrains('property_supplier_payment_term_id') def updated_by_payment_term(self): for rec in self: rec.user_payment_terms_purchase = self.env.user.id rec.date_payment_terms_purchase = datetime.utcnow() @api.onchange('site_id') def _onchange_site_id(self): for rec in self: if not rec.site_id: continue site = rec.site_id rec.street = site.street rec.street2 = site.street2 rec.city = site.city rec.state_id = site.state_id rec.country_id = site.country_id rec.zip = site.zip def get_main_parent(self): partner = self while partner.parent_id: partner = partner.parent_id return partner def _compute_main_parent_id(self): for partner in self: partner.main_parent_id = partner.get_main_parent() def get_child_ids(self): partner = self.env['res.partner'].search([('id', '=', self.id)], limit=1) partner_child_ids = [x['id'] for x in partner.child_ids] + [partner.id] if partner.parent_id: partner_child_ids += [x['id'] for x in partner.parent_id.child_ids] partner_child_ids += [partner.parent_id.id] return partner_child_ids def get_approve_partner_ids(self, type=False): parent = self.parent_id or self partners = self.search([('parent_id', '=', parent.id), ('web_role', '!=', False)]) if type == 'email_comma_sep': return ",".join([x.email for x in partners]) return partners @api.constrains('kota_id') def update_product_solr_flag(self): for partner in self: partner.city = partner.kota_id.name def unlink(self): if self._name == 'res.partner': raise UserError('Maaf anda tidak bisa delete contact') @api.onchange('customer_type') def _onchange_customer_type(self): if self.customer_type == 'nonpkp': self.npwp = '00.000.000.0-000.000' elif self.customer_type == 'pkp': self.npwp = '00.000.000.0-000.000' def get_check_payment_term(self): self.ensure_one() partner = self.parent_id or self return partner.property_payment_term_id.name if partner.property_payment_term_id.id else 'Cash Before Delivery (C.B.D)' @api.constrains('nitku') def _onchange_nitku(self): if self.nitku: if not self.nitku.isdigit(): raise UserError("NITKU harus berupa angka.") if len(self.nitku) != 22: raise UserError("NITKU harus memiliki tepat 22 angka.") @api.onchange('name') def _onchange_name(self): if self.company_type == 'person': self.nama_wajib_pajak = self.name def action_open_full_form(self): return { 'type': 'ir.actions.act_window', 'name': 'Partner', 'res_model': 'res.partner', 'res_id': self.id, 'view_mode': 'form', 'target': 'current', } def geocode_address(self): for rec in self: # Daftar field penting required_fields = { 'Alamat Jalan (street)': rec.street, 'Kelurahan': rec.kelurahan_id.name if rec.kelurahan_id else '', 'Kecamatan': rec.kecamatan_id.name if rec.kecamatan_id else '', 'Kota': rec.kota_id.name if rec.kota_id else '', 'Kode Pos': rec.zip, 'Provinsi': rec.state_id.name if rec.state_id else '', } # Cek jika ada yang kosong missing = [label for label, val in required_fields.items() if not val] if missing: raise UserError( "Alamat tidak lengkap. Mohon lengkapi field berikut:\n- " + "\n- ".join(missing) ) # Susun alamat lengkap address = ', '.join([ required_fields['Alamat Jalan (street)'], required_fields['Kelurahan'], required_fields['Kecamatan'], required_fields['Kota'], required_fields['Kode Pos'], required_fields['Provinsi'], ]) # Ambil API Key api_key = "AIzaSyB7bG9aSNAJnSrj0Z7f1abFsqKVoiJfsPE" # api_key = self.env['ir.config_parameter'].sudo().get_param('google.maps.api_key') # if not api_key: # raise UserError("API Key Google Maps belum dikonfigurasi. Silakan isi melalui Settings.") # Request ke Google Maps url = f'https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={api_key}' response = requests.get(url) if response.ok: result = response.json() if result.get('results'): location = result['results'][0]['geometry']['location'] formatted_address = result['results'][0].get('formatted_address', '') rec.latitude = location['lat'] rec.longtitude = location['lng'] rec.address_map = formatted_address # ✅ Simpan alamat lengkap else: raise UserError("Tidak ditemukan hasil geocode untuk alamat tersebut.") else: raise UserError("Permintaan ke Google Maps gagal. Periksa koneksi internet atau API Key.") # def _update_address_from_coords(self): # for rec in self: # if rec.latitude and rec.longtitude: # try: # components, formatted, parsed = rec._reverse_geocode(rec.latitude, rec.longtitude) # if not parsed: # continue # updates = { # 'street': parsed.get('road') or '', # 'zip': parsed.get('postcode') or '', # 'address_map': formatted or '', # } # if self.env.context.get('overwrite_street_from_pin', False): # updates['street'] = parsed.get('road') or rec.street # state = self.env['res.country.state'].search([('name', 'ilike', parsed.get('state'))], limit=1) # if state: # updates['state_id'] = state.id # kota = self.env['vit.kota'].search([('name', 'ilike', parsed.get('city'))], limit=1) # if kota: # updates['kota_id'] = kota.id # kec = self.env['vit.kecamatan'].search([('name', 'ilike', parsed.get('district'))], limit=1) # if kec: # updates['kecamatan_id'] = kec.id # kel = self.env['vit.kelurahan'].search([('name', 'ilike', parsed.get('suburb'))], limit=1) # if kel: # updates['kelurahan_id'] = kel.id # rec.update(updates) # except Exception as e: # raise UserError(f"Gagal update alamat dari koordinat: {str(e)}") def _update_address_from_coords(self): for rec in self: if rec.latitude and rec.longtitude: try: components, formatted, parsed = rec._reverse_geocode(rec.latitude, rec.longtitude) if not parsed: continue updates = { 'address_map': formatted or rec.address_map or '', } # ❌ Jangan isi street kecuali diminta eksplisit via context if self.env.context.get('overwrite_street_from_pin', False): road = parsed.get('road') if road: updates['street'] = road # (Opsional) admin area & zip hanya jika kosong agar tidak menimpa input user if (not rec.zip) and parsed.get('postcode'): updates['zip'] = parsed.get('postcode') state_name = parsed.get('state') if state_name and not rec.state_id: state = self.env['res.country.state'].search([('name', 'ilike', state_name)], limit=1) if state: updates['state_id'] = state.id city_name = parsed.get('city') if city_name and not rec.kota_id: kota = self.env['vit.kota'].search([('name', 'ilike', city_name)], limit=1) if kota: updates['kota_id'] = kota.id dist_name = parsed.get('district') if dist_name and not rec.kecamatan_id: kec = self.env['vit.kecamatan'].search([('name', 'ilike', dist_name)], limit=1) if kec: updates['kecamatan_id'] = kec.id sub_name = parsed.get('suburb') if sub_name and not rec.kelurahan_id: kel = self.env['vit.kelurahan'].search([('name', 'ilike', sub_name)], limit=1) if kel: updates['kelurahan_id'] = kel.id if updates: rec.update(updates) except Exception as e: raise UserError(f"Gagal update alamat dari koordinat: {str(e)}") def _reverse_geocode(self, lat, lng): api_key = "AIzaSyB7bG9aSNAJnSrj0Z7f1abFsqKVoiJfsPE" # api_key = self.env['ir.config_parameter'].sudo().get_param('google.maps.api_key') # if not api_key: # raise UserError("API Key Google Maps belum dikonfigurasi.") url = f'https://maps.googleapis.com/maps/api/geocode/json?latlng={lat},{lng}&key={api_key}' response = requests.get(url) if response.ok: result = response.json() if result.get('results'): components = result['results'][0]['address_components'] formatted = result['results'][0]['formatted_address'] return components, formatted, self._parse_google_address(components) return {}, '', {} def _parse_google_address(self, components): def get(types): for comp in components: if types in comp['types']: return comp['long_name'] return '' street_number = get('street_number') route = get('route') neighborhood = get('neighborhood') # Bisa jadi nama RW subpremise = get('subpremise') # Bisa jadi no kamar/ruko # Gabungkan informasi jalan road = " ".join(filter(None, [route, street_number, subpremise, neighborhood])) return { 'road': road.strip(), 'postcode': get('postal_code'), 'state': get('administrative_area_level_1'), 'city': get('administrative_area_level_2') or get('locality'), 'district': get('administrative_area_level_3'), 'suburb': get('administrative_area_level_4'), 'formatted': get('formatted_address'), }