from .. import controller from odoo import http from odoo.http import request, Response from odoo import fields import json import base64 import mimetypes class Partner(controller.Controller): _name = 'res.partner' prefix = '/api/v1/' def get_partner_child_ids(self, partner_id): partner = request.env[self._name].search([('id', '=', partner_id)], limit=1) if not partner.parent_id: 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 @http.route(prefix + 'partner//list/site', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_list_partner_by_id(self, **kw): params = self.get_request_params(kw, { 'id': ['required', 'number'] }) if not params['valid']: return self.response(code=400, description=params) partner_id = params['value']['id'] partner_child_ids = self.get_partner_child_ids(partner_id) partners = request.env['res.partner'].search([('id', 'in', partner_child_ids)]) site_names = set() for partner in partners: if partner.site_id: site_names.add(partner.site_id.name) data = { 'sites': list(site_names) } return self.response(data) @http.route(prefix + 'partner//address', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_partner_address_by_id(self, **kw): params = self.get_request_params(kw, { 'id': ['required', 'number'] }) if not params['valid']: return self.response(code=400, description=params) partner = request.env[self._name].search([('id', '=', params['value']['id'])]) partner = request.env['res.users'].api_address_response(partner) return self.response(partner) @http.route(prefix + 'partner//address', type="json", auth='public', methods=['PUT', 'OPTIONS'], csrf=False, cors='*') @controller.Controller.must_authorized() def write_partner_address_by_id(self, id, **kw): headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': '*' } if request.httprequest.method == 'OPTIONS': return Response(status=200, headers=headers) try: params = self.get_request_params(request.jsonrequest, { 'id': ['required', 'number'], 'type': ['default:other'], 'name': ['required'], 'email': ['required'], 'mobile': ['required'], 'phone': [''], 'street': ['required'], 'state_id': ['required', 'alias:state_id'], 'city_id': ['required', 'alias:kota_id'], 'district_id': ['alias:kecamatan_id'], 'sub_district_id': ['alias:kelurahan_id', 'exclude_if_null'], 'zip': ['required'], 'longtitude': '', 'latitude': '', 'address_map': [], 'alamat_lengkap_text': [] }) if not params['valid']: return {'headers': headers, 'code': 400, 'description': params} partner = request.env['res.partner'].sudo().search([('id', '=', id)], limit=1) if not partner: return {'headers': headers, 'code': 404, 'description': 'User not found'} vals = dict(params['value']) vals.pop('id', None) use_pin = bool(request.jsonrequest.get('use_pin')) if not use_pin: vals.pop('address_map', None) vals.pop('latitude', None) vals.pop('longtitude', None) else: lat = vals.get('latitude') lng = vals.get('longtitude') if not lat or not lng or float(lat) == 0.0 or float(lng) == 0.0: vals.pop('latitude', None) vals.pop('longtitude', None) partner.write(vals) return {'id': partner.id, 'headers': headers} except Exception as e: return {'headers': headers, 'code': 500, 'description': f'Internal Error: {str(e)}'} @http.route(prefix + 'partner/address', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def create_partner_address(self, **kw): params = self.get_request_params(kw, { 'parent_id': ['required', 'number'], 'type': ['default:other'], 'name': ['required'], 'email': ['required'], 'mobile': ['required'], 'phone': [''], 'street': ['required'], 'state_id': ['required', 'number', 'alias:state_id'], 'city_id': ['required', 'number', 'alias:kota_id'], 'district_id': ['number', 'alias:kecamatan_id'], 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], 'longtitude': '', 'latitude': '', 'address_map': [], 'zip': ['required'] }) if not params['valid']: return self.response(code=400, description=params) partner = request.env[self._name].create([ params['value'] ]) return self.response({ 'id': partner.id, }) @http.route(prefix + 'partner/', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_by_id(self, id, **kw): try: # Ambil data JSON langsung request_data = kw partner = request.env['res.partner'].sudo().browse(id) if not partner.exists(): return self.response({ 'code': 400, 'description': 'Partner not found' }) partner_params = self.get_request_params(request_data, { 'tax_name': ['alias:nama_wajib_pajak'], 'company_type_id': [''], 'industry_id': ['number'], 'npwp': [], 'alamat_lengkap_text': [], 'street': [], 'email': [], 'mobile': [] }) if not partner_params['valid']: return self.response({ 'code': 400, 'description': partner_params }) partner_values = partner_params['value'] if 'id_user' in request_data: user_params = self.get_request_params(request_data, { 'id_user': ['required', 'number'], 'company_type_id': [''], 'industry_id': ['number'], 'tax_name': ['alias:nama_wajib_pajak'], 'npwp': [], 'alamat_lengkap_text': [], }) if not user_params['valid']: return self.response({ 'code': 400, 'description': user_params }) user = request.env['res.partner'].sudo().browse(int(user_params['value']['id_user'])) if user.exists(): user_values = user_params['value'] if not user_values.get('tax_name'): user_values['nama_wajib_pajak'] = user_values.get('name', user.name) if not user_values.get('alamat_lengkap_text'): user_values['alamat_lengkap_text'] = user_values.get('street', user.street) if not user_values.get('npwp'): user_values['npwp'] = "00.000.000.0-000.000" user_values_filtered = {k: v for k, v in user_values.items() if k != 'id_user' and v is not None} if user_values_filtered: user.write(user_values_filtered) partner.write(partner_values) return self.response({ 'partner_id': partner.id }) except Exception as e: return self.response({ 'code': 500, 'description': f'Internal Error: {str(e)}' }) @http.route(prefix + 'partner/industry', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_partner_industry(self): partner_industry = request.env['res.partner.industry'].search([]) data = [] for industry in partner_industry: full_name = industry.full_name category = full_name.title() if full_name else '-' data.append({ 'id': industry.id, 'name': industry.name, 'category': category }) return self.response(data) @http.route(prefix + 'partner/company_type', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_partner_company_type(self): partner_company_type = request.env['res.partner.company_type'].search([]) data = [] for company_type in partner_company_type: data.append({ 'id': company_type.id, 'name': company_type.name }) return self.response(data) @http.route(prefix + 'partner/payment_term', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_partner_payment_term(self): partner_industry = request.env['account.payment.term'].search([]) data = [] for industry in partner_industry: if 'tempo' in industry.name.lower(): data.append({ 'id': industry.id, 'name': industry.name }) return self.response(data) @http.route(prefix + 'partner/detail-tempo/', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_partner_detail_tempo(self, **kw): params = self.get_request_params(kw, { 'id': ['required', 'number'] }) pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', params['value']['id'])], limit=1) if not pengajuan_tempo: return self.response(code=404, description='pengajuan tempo not found') pengajuan_tempo = request.env['res.partner'].api_single_response(pengajuan_tempo) return self.response(pengajuan_tempo) @http.route(prefix + 'check//tempo', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_check_tempo_partner(self, **kw): partner_id = int(kw.get('partner_id')) partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) if not partner: return self.response(code=404, description='Partner not found') partner = partner.parent_id or partner payment_term = ( partner.previous_payment_term_id if partner.is_cbd_locked else partner.property_payment_term_id ) # if any(line.days == 0 for line in partner.property_payment_term_id.line_ids): if any(line.days == 0 for line in payment_term.line_ids): return self.response(code=402, description='Partner not tempo') domain_result_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')] domain_result_jatuh_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted'), ('invoice_date_due', '<', fields.Date.today())] domain_orders = [('partner_id', '=', partner.id), ('invoice_status', '!=', 'invoiced'), ('state', '=', 'sale')] result_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_tempo)) result_tempo_total = request.env['account.move'].search_count(domain_result_tempo) result_jatuh_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_jatuh_tempo)) result_jatuh_tempo_total = request.env['account.move'].search_count(domain_result_jatuh_tempo) orders = request.env['sale.order'].search(domain_orders) orders_total = request.env['sale.order'].search_count(domain_orders) total_amount = sum(order.amount_total for order in orders) remaining_limit = partner.blocking_stage - result_tempo if partner.active_limit else None data = { 'name': partner.name, 'payment_term': payment_term.name, 'amount_due': result_tempo, 'amount_due_total': result_tempo_total, 'amount_jatuh_tempo_total': result_jatuh_tempo_total, 'amount_jatuh_tempo': result_jatuh_tempo, 'amount_due_sale': total_amount, 'amount_due_sale_total': orders_total, 'remaining_limit': remaining_limit } return self.response(data) @http.route(prefix + 'check//tempo_progress', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_check_tempo_partner_progres(self, **kw): partner_id = int(kw.get('partner_id')) partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1) pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', partner.id)], limit=1) if not pengajuan_tempo: return self.response(code=404, description='Partner not found') data = True if pengajuan_tempo.id else False return self.response(data) @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_pengajuan_tempo(self, **kw): try: id = int(kw.get('partner_id')) user_id = int(kw.get('user_id')) tempo_request = True if kw.get('tempo_request') == 'true' else False pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', user_id)], limit=1) user = request.env['res.partner'].search([('id', '=', user_id)], limit=1) company_name = kw.get('name', pengajuan_tempo.name_tempo.name) partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) user_account = self.get_user_by_email(user.email) dokumen_prosedur = False if kw.get('formDokumenProsedur') and kw.get('formDokumenProsedur') != 'false': dokumen_prosedur = kw.get('formDokumenProsedur') params = self.get_request_params(kw, { # informasi perusahaan # 'name': ['required', 'alias:name_tempo'], 'industryId': ['alias:industry_id_tempo'], 'street': ['alias:street_tempo'], 'state': ['alias:state_id_tempo'], 'city': ['alias:city_id_tempo'], 'district': ['alias:district_id_tempo'], 'subDistrict': ['alias:subDistrict_id_tempo'], 'zip': ['alias:zip_tempo'], 'mobile': ['alias:mobile_tempo'], 'bankName': ['alias:bank_name_tempo'], 'accountName': ['alias:account_name_tempo'], 'accountNumber': ['alias:account_number_tempo'], 'website': ['alias:website_tempo'], 'estimasi': ['alias:estimasi_tempo'], 'portal': ['alias:portal'], 'bersedia': ['alias:bersedia'], 'tempoDuration': ['alias:tempo_duration'], 'tempoLimit': ['alias:tempo_limit'], # informasi perusahaan 'direkturTittle': ['alias:direktur_tittle'], 'direkturName': ['alias:direktur_name'], 'direkturMobile': ['alias:direktur_mobile'], 'direkturEmail': ['alias:direktur_email'], 'purchasingTittle': ['alias:purchasing_tittle'], 'purchasingName': ['alias:purchasing_name'], 'purchasingMobile': ['alias:purchasing_mobile'], 'purchasingEmail': ['alias:purchasing_email'], 'financeTittle': ['alias:finance_tittle'], 'financeName': ['alias:finance_name'], 'financeMobile': ['alias:finance_mobile'], 'financeEmail': ['alias:finance_email'], # Pengiriman 'PICTittle': ['alias:pic_tittle'], 'PICBarangMobile': ['alias:pic_mobile'], 'PICName': ['alias:pic_name'], 'streetPengiriman': ['alias:street_pengiriman'], 'statePengiriman': ['alias:state_id_pengiriman'], 'cityPengiriman': ['alias:city_id_pengiriman'], 'districtPengiriman': ['alias:district_id_pengiriman'], 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], 'zipPengiriman': ['alias:zip_pengiriman'], 'invoicePicTittle': ['alias:invoice_pic_tittle'], 'invoicePicMobile': ['alias:invoice_pic_mobile'], 'invoicePic': ['alias:invoice_pic'], 'streetInvoice': ['alias:street_invoice'], 'stateInvoice': ['alias:state_id_invoice'], 'cityInvoice': ['alias:city_id_invoice'], 'districtInvoice': ['alias:district_id_invoice'], 'subDistrictInvoice': ['alias:subDistrict_id_invoice'], 'zipInvoice': ['alias:zip_invoice'], 'isSameAddrees':['alias:is_same_address'], 'isSameAddreesStreet':['alias:is_same_address_street'], }) # # Konversi nilai 'true' ke boolean True # is_same_address = kw.get('isSameAddrees', 'false').lower() == 'true' # is_same_address_street = kw.get('isSameAddreesStreet', 'false').lower() == 'true' # # # Tambahkan nilai yang dikonversi ke params # if 'isSameAddress' in kw: # params['value']['is_same_address'] = is_same_address # if 'is_same_address_street' in kw: # params['value']['is_same_address_street'] = is_same_address_street if not params['valid']: return self.response(code=400, description=params) if params['value']['portal']: if params['value']['portal'] == 'ada': params['value']['portal'] = True else: params['value']['portal'] = False # Filter data baru yang dikirim (non-kosong, boolean False tetap masuk) new_data = {key: value for key, value in params['value'].items() if value != ''} if pengajuan_tempo: try: pengajuan_tempo.write(new_data) except Exception as e: return self.response(code=500, description=f'Error updating partner data: {str(e)}') else: try: pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data) pengajuan_tempo.partner_id = user_id except Exception as e: return self.response(code=500, description=f'Error creating partner data: {str(e)}') if partner_id: try: pengajuan_tempo.name_tempo = partner_id except Exception as e: return self.response(code=500, description=f'Error updating partner data: {str(e)}') # Prosedur Pengiriman if dokumen_prosedur: dokumen_prosedur = json.loads(dokumen_prosedur) mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) mimetype = mimetype or 'application/octet-stream' data = base64.b64decode(dokumen_prosedur['base64']) dokumen_prosedur_attachment = request.env['ir.attachment'].create({ 'name': dokumen_prosedur['name'], 'type': 'binary', 'datas': base64.b64encode(data), 'res_model': 'user.pengajuan.tempo', 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) pengajuan_tempo.dokumen_prosedur = [(6, 0, [dokumen_prosedur_attachment.id])] pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[dokumen_prosedur_attachment.id]) form_supplier_data = kw.get('formSupplier', False) if form_supplier_data: try: form_supplier_data = json.loads(form_supplier_data) supplier_ids_to_add = [] for item in form_supplier_data: supplier_name = item.get("supplier") pic_name = item.get("pic") phone = item.get("telepon") tempo_duration = item.get("durasiTempo") credit_limit = item.get("creditLimit") new_data = { 'name_supplier': supplier_name, 'pic_name': pic_name, 'phone': phone, 'tempo_duration': tempo_duration, 'credit_limit': credit_limit, } new_supplier_data = request.env['user.pengajuan.tempo.line'].create(new_data) supplier_ids_to_add.append(new_supplier_data.id) pengajuan_tempo.write({'supplier_ids': [(6, 0, supplier_ids_to_add)]}) except json.JSONDecodeError: return http.Response(status=400, json_body={'error': 'Invalid JSON format for formSupplier'}) category_produk_ids = kw.get('categoryProduk', False) category_ids = '' if category_produk_ids: try: category_ids = list(map(int, category_produk_ids.split(','))) pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] except Exception as e: return self.response(code=500, description=f'Unexpected error: {str(e)}') tukar_invoice_input = kw.get('tukarInvoiceInput') if tukar_invoice_input: pengajuan_tempo.tukar_invoice = tukar_invoice_input tukar_invoice_input_pembayaran = kw.get('tukarInvoiceInputPembayaran') if tukar_invoice_input_pembayaran: pengajuan_tempo.jadwal_bayar = tukar_invoice_input_pembayaran dokumen_kirim = [ 'Surat Tanda Terima Barang (STTB)', 'Good Receipt (GR)', 'Surat Terima Barang (STB)', 'Lembar Penerimaan Barang (LPB)' ] dokumen_kirim_barang_ids = kw.get('dokumenPengiriman') dokumen_kirim_input = kw.get('dokumenKirimInput', '') dokumen_kirim_barang_input = kw.get('dokumenPengirimanInput', '') dokumen_kirim_barang = [] if dokumen_kirim_barang_ids: dokumen_kirim_ids = list(map(int, dokumen_kirim_barang_ids.split(','))) dokumen_kirim_barang = [dokumen_kirim[i] for i in dokumen_kirim_ids if 0 <= i < len(dokumen_kirim)] if dokumen_kirim_input: input_items = [item.strip() for item in dokumen_kirim_input.split(',')] dokumen_kirim_barang.extend(item for item in input_items if item and item not in dokumen_kirim_barang) pengajuan_tempo.dokumen_kirim_input = dokumen_kirim_input if dokumen_kirim_barang: pengajuan_tempo.dokumen_pengiriman = ', '.join(dokumen_kirim_barang) if dokumen_kirim_barang_input: pengajuan_tempo.dokumen_pengiriman_input = dokumen_kirim_barang_input dokumen = [ 'Invoice Pembelian', 'Surat Jalan', 'Berita Acara Serah Terima (BAST)', 'Faktur Pajak', 'Good Receipt (GR)' ] dokumen_invoice = kw.get('dokumenPengirimanInvoice', '') if dokumen_invoice: pengajuan_tempo.dokumen_invoice = dokumen_invoice user_tempo_request = [] if tempo_request: user_tempo_request = request.env['user.pengajuan.tempo.request'].create({ 'user_id': id, 'pengajuan_tempo_id': pengajuan_tempo.id, 'user_company_id': partner_id.id, 'tempo_duration': pengajuan_tempo.tempo_duration.id, 'tempo_limit': pengajuan_tempo.tempo_limit, }) form_dokumen_data = kw.get('formDocs', False) if form_dokumen_data: try: form_dokumen = json.loads(form_dokumen_data) for dokumen in form_dokumen: if dokumen['details']['base64'] != '': mimetype, _ = mimetypes.guess_type(dokumen['details']['name']) mimetype = mimetype or 'application/octet-stream' data = base64.b64decode(dokumen['details']['base64']) sppkp_attachment = request.env['ir.attachment'].create({ 'name': dokumen['details']['name'], 'type': 'binary', 'datas': base64.b64encode(data), 'res_model': 'user.pengajuan.tempo', 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) if dokumen['documentName'] == 'dokumenNib': pengajuan_tempo.dokumen_nib = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenSiup': pengajuan_tempo.dokumen_siup = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenTdp': pengajuan_tempo.dokumen_tdp = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenSkdp': pengajuan_tempo.dokumen_skdp = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenSkt': pengajuan_tempo.dokumen_skt = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenNpwp': pengajuan_tempo.dokumen_npwp = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenSppkp': pengajuan_tempo.dokumen_sppkp = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenAktaPerubahan': pengajuan_tempo.dokumen_akta_perubahan = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenKtpDirut': pengajuan_tempo.dokumen_ktp_dirut = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenAktaPendirian': pengajuan_tempo.dokumen_akta_pendirian = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenLaporanKeuangan': pengajuan_tempo.dokumen_laporan_keuangan = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenFotoKantor': pengajuan_tempo.dokumen_foto_kantor = [(6, 0, [sppkp_attachment.id])] elif dokumen['documentName'] == 'dokumenTempatBekerja': pengajuan_tempo.dokumen_tempat_bekerja = [(6, 0, [sppkp_attachment.id])] formatted_text = ''.join([' ' + char if char.isupper() and i != 0 else char for i, char in enumerate(dokumen['documentName'])]) teks = formatted_text.strip().title() pengajuan_tempo.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) if tempo_request: user_tempo_request.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) except json.JSONDecodeError: return http.Response(status=400, json_body={'error': 'Invalid JSON format for formDokumen'}) if tempo_request: # pengajuan_tempo.user_id = id template = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_request_tempo_review') template.send_mail(pengajuan_tempo.id, force_send=True) template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales') template2.send_mail(pengajuan_tempo.id, force_send=True) if not pengajuan_tempo: return self.response(code=500, description="Failed to create or update pengajuan_tempo") return self.response({ 'id': pengajuan_tempo.id, 'user_id': user_id, }) except Exception as e: return self.response(code=500, description=f'Unexpected error: {str(e)}') def get_user_by_email(self, email): return request.env['res.users'].search([ ('login', '=', email), ('active', 'in', [True, False]) ])