From 3286de7f2d48f913ad1aa477b49155047ebb742c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 27 Aug 2025 13:11:17 +0700 Subject: Regenerate midtrans via website --- indoteknik_api/controllers/api_v1/sale_order.py | 128 +++++++++--------------- 1 file changed, 49 insertions(+), 79 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 374b49a2..d4038a64 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -116,14 +116,12 @@ class SaleOrder(controller.Controller): if not params['valid']: return self.response(code=400, description=params) - partner_child_ids = self.get_partner_child_ids( - params['value']['partner_id']) + partner_child_ids = self.get_partner_child_ids(params['value']['partner_id']) domain = [('partner_id', 'in', partner_child_ids)] context = params['value']['context'] if context == 'quotation': - domain += ["|", "|", ("state", "=", "draft"), - ("state", "=", "sent"), ("state", "=", "cancel")] + domain += ["|", "|", ("state", "=", "draft"), ("state", "=", "sent"), ("state", "=", "cancel")] if not context: domain += ["|", ("state", "=", "sale"), ("state", "=", "done")] @@ -135,39 +133,28 @@ class SaleOrder(controller.Controller): ('product_id.name', 'ilike', name), ('product_id.default_code', 'ilike', name), ]) - sale_order_ids_from_lines = order_lines.mapped('order_id.id') - domain += ['|', '|', ('name', 'ilike', name), ('partner_purchase_order_name', 'ilike', name), ('id', 'in', sale_order_ids_from_lines) ] - + if params['value']['site']: site = params['value']['site'].replace(' ', '%') - domain += [ - ('partner_id.site_id.name', 'ilike', '%' + site + '%') - ] + domain += [('partner_id.site_id.name', 'ilike', '%' + site + '%')] status = params['value'].get('status') if status: if status == 'quotation': domain += [('state', '=', 'draft')] domain += [('approval_status', '=', False)] - elif status == 'cancel': domain += [('state', '=', 'cancel')] - elif status == 'diproses': - domain += [ - ('state', '=', 'draft'), - ('approval_status', 'in', ['pengajuan1', 'pengajuan2']), - ] - + domain += [('state', '=', 'draft'), ('approval_status', 'in', ['pengajuan1', 'pengajuan2'])] elif status in ['dikemas', 'dikirim', 'selesai', 'partial']: domain += [('state', '=', 'sale')] - elif status == 'all': domain += [] @@ -179,7 +166,7 @@ class SaleOrder(controller.Controller): elif params['value']['sort'] == 'desc': order = 'amount_total desc' - # Filter berdasarkan tanggal order + # Filter tanggal try: if params['value']['startDate'] and params['value']['endDate']: start_date = datetime.strptime(params['value']['startDate'], '%d/%m/%Y').strftime('%Y-%m-%d 00:00:00') @@ -190,22 +177,17 @@ class SaleOrder(controller.Controller): domain.append(('date_order', '>=', start_date)) domain.append(('date_order', '<=', end_date)) - except ValueError: return self.response(code=400, description="Invalid date format. Use 'DD/MM/YYYY'.") + sale_orders = request.env['sale.order'].search(domain, order=order) - - sale_orders = request.env['sale.order'].search( - domain, order=order) + # Filter status pengiriman (tetap sama) status = params['value'].get('status') if status in ['dikemas', 'dikirim', 'selesai', 'partial']: filtered_orders = [] for sale_order in sale_orders: - bu_pickings = [ - p for p in sale_order.picking_ids - if p.picking_type_id and p.picking_type_id.id == 29 - ] + bu_pickings = [p for p in sale_order.picking_ids if p.picking_type_id and p.picking_type_id.id == 29] total = len(bu_pickings) done_pickings = [p for p in bu_pickings if p.state == 'done'] done_with_driver = [p for p in done_pickings if p.sj_return_date] @@ -217,69 +199,57 @@ class SaleOrder(controller.Controller): filtered_orders.append(sale_order) elif status == 'selesai' and len(done_pickings) == total and len(done_pickings) > 0 and len(done_with_driver) == total: filtered_orders.append(sale_order) - elif status == 'partial' and ( - len(done_pickings) != total or - (done_with_driver and done_without_driver) - ): + elif status == 'partial' and (len(done_pickings) != total or (done_with_driver and done_without_driver)): filtered_orders.append(sale_order) else: filtered_orders = sale_orders filtered_orders_paginated = filtered_orders[offset: offset + limit] + # === Tambahan: payment summary per SO === + CBD_PAYMENT_TERM_ID = 26 + ALLOWED_UNSETTLED = {'', 'expire', 'pending', 'challenge', 'cancel'} # belum settlement + def _is_website_order(so): + # Sesuaikan kalau perlu spesifik id + # return bool(so.source_id and so.source_id.id == 59) + name = (so.source_id and so.source_id.name or '').lower() + return 'web' in name or 'site' in name or bool(so.source_id) + + sale_orders_payload = [] + for so in filtered_orders_paginated: + item = request.env['sale.order'].api_v1_single_response(so) + + so_state = so.state or '' + pay_term_id = so.payment_term_id.id if so.payment_term_id else None + pay_status = (getattr(so, 'payment_status', '') or '').strip().lower() + from_web = _is_website_order(so) + + is_quotation = so_state in ('draft', 'sent') + is_cbd = (pay_term_id == CBD_PAYMENT_TERM_ID) + ok_status = (pay_status in ALLOWED_UNSETTLED) + + eligible = bool(is_quotation and is_cbd and ok_status and from_web) + + # tambahkan field baru (camelCase biar enak dipakai di Next) + item.update({ + 'eligibleContinue': eligible, + 'paymentSummary': { + 'eligible': eligible, + 'soState': so_state, + 'paymentTermId': pay_term_id, + 'paymentStatus': pay_status, + 'sourceId': so.source_id.id if so.source_id else None, + 'redirectUrl': getattr(so, 'payment_link_midtrans', '') or '', + } + }) + sale_orders_payload.append(item) + data = { 'sale_order_total': len(filtered_orders), - 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in filtered_orders_paginated] + 'sale_orders': sale_orders_payload } - return self.response(data) - @http.route(PREFIX_PARTNER + 'sale_order/', auth='public', method=['GET', 'OPTIONS']) - @controller.Controller.must_authorized() - def partner_get_sale_order_detail(self, **kw): - params = self.get_request_params(kw, { - 'partner_id': ['number'], - 'id': ['number'], - }) - if not params['valid']: - return self.response(code=400, description=params) - - partner_child_ids = self.get_partner_child_ids(params['value']['partner_id']) - domain = [('id', '=', params['value']['id']), ('partner_id', 'in', partner_child_ids)] - data = {} - sale_order = request.env['sale.order'].search(domain) - if sale_order: - data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail') - if sale_order.expected_ready_to_ship: - bulan_id = [ - "Januari", "Februari", "Maret", "April", "Mei", "Juni", - "Juli", "Agustus", "September", "Oktober", "November", "Desember" - ] - tanggal = sale_order.expected_ready_to_ship.day - bulan = bulan_id[sale_order.expected_ready_to_ship.month - 1] - tahun = sale_order.expected_ready_to_ship.year - data['expected_ready_to_ship'] = f"{tanggal} {bulan} {tahun}" - if sale_order.eta_date_start: - bulan_id = [ - "Januari", "Februari", "Maret", "April", "Mei", "Juni", - "Juli", "Agustus", "September", "Oktober", "November", "Desember" - ] - tanggal = sale_order.eta_date_start.day - bulan = bulan_id[sale_order.eta_date_start.month - 1] - tahun = sale_order.eta_date_start.year - data['eta_date_start'] = f"{tanggal} {bulan} {tahun}" - - if sale_order.eta_date: - bulan_id = [ - "Januari", "Februari", "Maret", "April", "Mei", "Juni", - "Juli", "Agustus", "September", "Oktober", "November", "Desember" - ] - tanggal = sale_order.eta_date.day - bulan = bulan_id[sale_order.eta_date.month - 1] - tahun = sale_order.eta_date.year - data['eta_date_end'] = f"{tanggal} {bulan} {tahun}" - - return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order//checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized(private=True, private_key='partner_id') -- cgit v1.2.3 From 3a1c84158be32915ab9a30877e26c6e48733ba2b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 28 Aug 2025 10:02:36 +0700 Subject: push --- indoteknik_api/controllers/api_v1/sale_order.py | 44 +++++++++++++++---------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index d4038a64..fbbb9479 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -206,40 +206,47 @@ class SaleOrder(controller.Controller): filtered_orders_paginated = filtered_orders[offset: offset + limit] - # === Tambahan: payment summary per SO === + # === Tambahan ringkasan + (opsional) auto-regenerate link Midtrans === CBD_PAYMENT_TERM_ID = 26 - ALLOWED_UNSETTLED = {'', 'expire', 'pending', 'challenge', 'cancel'} # belum settlement + ALLOWED_UNSETTLED = {'', 'expire', 'cancel', 'null', '[null]'} # sesuai syaratmu + def _is_website_order(so): - # Sesuaikan kalau perlu spesifik id - # return bool(so.source_id and so.source_id.id == 59) - name = (so.source_id and so.source_id.name or '').lower() - return 'web' in name or 'site' in name or bool(so.source_id) + # kalau source website fix id=59, ini paling aman + return bool(so.source_id and so.source_id.id == 59) sale_orders_payload = [] for so in filtered_orders_paginated: item = request.env['sale.order'].api_v1_single_response(so) - so_state = so.state or '' - pay_term_id = so.payment_term_id.id if so.payment_term_id else None - pay_status = (getattr(so, 'payment_status', '') or '').strip().lower() - from_web = _is_website_order(so) + # ---- 4 syarat kamu ---- + approval_ok = (so.approval_status in ('pengajuan1', 'pengajuan2')) + source_ok = _is_website_order(so) + term_ok = bool(so.payment_term_id and so.payment_term_id.id == CBD_PAYMENT_TERM_ID) + pay_status = (getattr(so, 'payment_status', '') or '').strip().lower() + payment_ok = (pay_status in ALLOWED_UNSETTLED) - is_quotation = so_state in ('draft', 'sent') - is_cbd = (pay_term_id == CBD_PAYMENT_TERM_ID) - ok_status = (pay_status in ALLOWED_UNSETTLED) + eligible = bool(approval_ok and source_ok and term_ok and payment_ok) - eligible = bool(is_quotation and is_cbd and ok_status and from_web) + # Jika eligible & link kosong, auto-generate SEKARANG (tanpa endpoint baru) + redirect_url = getattr(so, 'payment_link_midtrans', '') or '' + if eligible and not redirect_url: + try: + so.sudo().generate_payment_link_midtrans_sales_order() + redirect_url = getattr(so, 'payment_link_midtrans', '') or '' + except Exception as e: + # jangan gagalkan list; cukup lanjut tanpa link + _logger.warning(f'Autogenerate Midtrans gagal untuk SO {so.id}: {e}') - # tambahkan field baru (camelCase biar enak dipakai di Next) + # sisipkan ke payload (pakai camelCase agar enak di FE) item.update({ 'eligibleContinue': eligible, 'paymentSummary': { 'eligible': eligible, - 'soState': so_state, - 'paymentTermId': pay_term_id, + 'approvalStatus': so.approval_status or '', 'paymentStatus': pay_status, + 'paymentTermId': so.payment_term_id.id if so.payment_term_id else None, 'sourceId': so.source_id.id if so.source_id else None, - 'redirectUrl': getattr(so, 'payment_link_midtrans', '') or '', + 'redirectUrl': redirect_url, } }) sale_orders_payload.append(item) @@ -251,6 +258,7 @@ class SaleOrder(controller.Controller): return self.response(data) + @http.route(PREFIX_PARTNER + 'sale_order//checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized(private=True, private_key='partner_id') def partner_checkout_sale_order_by_id(self, **kw): -- cgit v1.2.3 From 576c49b3260e9d34c86e2da04461dff4bb16e0de Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 28 Aug 2025 10:46:34 +0700 Subject: Push --- indoteknik_api/controllers/api_v1/sale_order.py | 48 ++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index fbbb9479..b48c5a34 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -208,7 +208,7 @@ class SaleOrder(controller.Controller): # === Tambahan ringkasan + (opsional) auto-regenerate link Midtrans === CBD_PAYMENT_TERM_ID = 26 - ALLOWED_UNSETTLED = {'', 'expire', 'cancel', 'null', '[null]'} # sesuai syaratmu + ALLOWED_UNSETTLED = {'', 'expire', 'cancel'} # sesuai syaratmu def _is_website_order(so): # kalau source website fix id=59, ini paling aman @@ -256,7 +256,53 @@ class SaleOrder(controller.Controller): 'sale_orders': sale_orders_payload } return self.response(data) + + @http.route(PREFIX_PARTNER + 'sale_order/', auth='public', method=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def partner_get_sale_order_detail(self, **kw): + params = self.get_request_params(kw, { + 'partner_id': ['number'], + 'id': ['number'], + }) + if not params['valid']: + return self.response(code=400, description=params) + partner_child_ids = self.get_partner_child_ids(params['value']['partner_id']) + domain = [('id', '=', params['value']['id']), ('partner_id', 'in', partner_child_ids)] + data = {} + sale_order = request.env['sale.order'].search(domain) + if sale_order: + data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail') + if sale_order.expected_ready_to_ship: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.expected_ready_to_ship.day + bulan = bulan_id[sale_order.expected_ready_to_ship.month - 1] + tahun = sale_order.expected_ready_to_ship.year + data['expected_ready_to_ship'] = f"{tanggal} {bulan} {tahun}" + if sale_order.eta_date_start: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date_start.day + bulan = bulan_id[sale_order.eta_date_start.month - 1] + tahun = sale_order.eta_date_start.year + data['eta_date_start'] = f"{tanggal} {bulan} {tahun}" + + if sale_order.eta_date: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date.day + bulan = bulan_id[sale_order.eta_date.month - 1] + tahun = sale_order.eta_date.year + data['eta_date_end'] = f"{tanggal} {bulan} {tahun}" + + return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order//checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) -- cgit v1.2.3 From c6f2111d3872604c6cdd0901c1ee2fde5edb59de Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 28 Aug 2025 13:39:55 +0700 Subject: Done --- indoteknik_api/controllers/api_v1/sale_order.py | 13 ++++++++++++- indoteknik_api/models/sale_order.py | 14 +++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index b48c5a34..2d697ae1 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -151,8 +151,19 @@ class SaleOrder(controller.Controller): domain += [('approval_status', '=', False)] elif status == 'cancel': domain += [('state', '=', 'cancel')] + elif status == 'belum_bayar': + domain += [ + ('state', '=', 'draft'), + ('approval_status', 'in', ['pengajuan1', 'pengajuan2']), + ('payment_status', 'in', [False, None, '', 'pending' ,'expire']) + ] elif status == 'diproses': - domain += [('state', '=', 'draft'), ('approval_status', 'in', ['pengajuan1', 'pengajuan2'])] + domain += [ + ('state', '=', 'draft'), + ('approval_status', 'in', ['pengajuan1', 'pengajuan2']), + ('payment_status', '!=', False), + ('payment_status', 'not in', ['', 'pending', 'expire']), + ] elif status in ['dikemas', 'dikirim', 'selesai', 'partial']: domain += [('state', '=', 'sale')] elif status == 'all': diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 9be03927..c59dead9 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -60,13 +60,17 @@ class SaleOrder(models.Model): # 'tracking_number': picking.delivery_tracking_no or '', # 'delivered': picking.waybill_id.delivered or picking.driver_arrival_date != False or picking.sj_return_date != False, }) + if sale_order.state == 'cancel': data['status'] = 'cancel' - if sale_order.state == 'draft' and sale_order.approval_status == False: - data['status'] = 'draft' - if sale_order.state == 'draft' and sale_order.approval_status in ['pengajuan1', 'pengajuan2']: - data['status'] = 'waiting' - + elif sale_order.state == 'draft': + if not sale_order.approval_status: + data['status'] = 'draft' + elif sale_order.approval_status in ('pengajuan1', 'pengajuan2'): + if sale_order.payment_status in ('', 'pending', False, None, 'expire'): + data['status'] = 'belum_bayar' + elif sale_order.payment_status not in ['', 'pending', False, None, 'expire']: + data['status'] = 'waiting' if sale_order.state == 'sale': bu_pickings = [ -- cgit v1.2.3 From d672d8745f5157d4cf3ff17907a3ca0881b68901 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 28 Aug 2025 14:24:48 +0700 Subject: Show bayar sekarang in detail transaction --- indoteknik_api/controllers/api_v1/sale_order.py | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 2d697ae1..16fcd449 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -284,6 +284,43 @@ class SaleOrder(controller.Controller): sale_order = request.env['sale.order'].search(domain) if sale_order: data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail') + + CBD_PAYMENT_TERM_ID = 26 + ALLOWED_UNSETTLED = {'', 'expire', 'pending'} # kalau mau ikutkan 'pending', tinggal tambahkan di set ini + + def _is_website_order(so): + # aman kalau source website fix id=59 + return bool(so.source_id and so.source_id.id == 59) + + pay_status = (getattr(sale_order, 'payment_status', '') or '').strip().lower() + eligible = ( + sale_order.approval_status in ('pengajuan1', 'pengajuan2') and + _is_website_order(sale_order) and + sale_order.payment_term_id and sale_order.payment_term_id.id == CBD_PAYMENT_TERM_ID and + pay_status in ALLOWED_UNSETTLED + ) + + redirect_url = getattr(sale_order, 'payment_link_midtrans', '') or '' + if eligible and not redirect_url: + try: + sale_order.sudo().generate_payment_link_midtrans_sales_order() + redirect_url = getattr(sale_order, 'payment_link_midtrans', '') or '' + except Exception as e: + _logger.warning(f'Autogenerate Midtrans gagal untuk SO {sale_order.id}: {e}') + + # sisipkan ke payload (snake_case; FE kamu sudah biasa auto-camelCase) + data.update({ + 'eligible_continue': eligible, + 'payment_summary': { + 'eligible': eligible, + 'approval_status': sale_order.approval_status or '', + 'payment_status': pay_status, + 'payment_term_id': sale_order.payment_term_id.id if sale_order.payment_term_id else None, + 'source_id': sale_order.source_id.id if sale_order.source_id else None, + 'redirect_url': redirect_url, + } + }) + if sale_order.expected_ready_to_ship: bulan_id = [ "Januari", "Februari", "Maret", "April", "Mei", "Juni", -- cgit v1.2.3 From 101948d6029b06a69759b8f246f1744312f035c0 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 29 Aug 2025 10:52:19 +0700 Subject: (andri) add is locked CBD jika ada customer yang sudah jatuh tempo --- indoteknik_custom/models/account_move.py | 28 +++++++++++++++++++++++ indoteknik_custom/models/approval_payment_term.py | 3 ++- indoteknik_custom/models/res_partner.py | 15 ++++++++---- indoteknik_custom/models/sale_order.py | 9 ++++++++ indoteknik_custom/views/res_partner.xml | 1 + indoteknik_custom/views/sale_order.xml | 9 ++++++++ 6 files changed, 59 insertions(+), 6 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index c44cad78..55e640e4 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -105,6 +105,34 @@ class AccountMove(models.Model): tracking=True ) + def _check_and_lock_cbd(self): + cbd_term = self.env['account.payment.term'].browse(26) + today = date.today() + + # Cari semua invoice overdue + overdue_invoices = self.search([ + ('move_type', '=', 'out_invoice'), + ('state', '=', 'posted'), + ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), + ('invoice_date_due', '!=', False), + ('invoice_date_due', '<=', today - timedelta(days=30)), + ], limit=3) + + _logger.info(f"Found {len(overdue_invoices)} overdue invoices for CBD lock check.") + _logger.info(f"Overdue Invoices: {overdue_invoices.mapped('name')}") + + # Ambil partner unik dari invoice + partners_to_lock = overdue_invoices.mapped('partner_id').filtered(lambda p: not p.is_cbd_locked) + _logger.info(f"Partners to lock: {partners_to_lock.mapped('name')}") + + # Lock hanya partner yang belum locked + if partners_to_lock: + partners_to_lock.write({ + 'is_cbd_locked': True, + 'property_payment_term_id': cbd_term.id, + }) + + def compute_partial_payment(self): for move in self: if move.amount_total_signed > 0 and move.amount_residual_signed > 0 and move.payment_state == 'partial': diff --git a/indoteknik_custom/models/approval_payment_term.py b/indoteknik_custom/models/approval_payment_term.py index 8618856a..08d91738 100644 --- a/indoteknik_custom/models/approval_payment_term.py +++ b/indoteknik_custom/models/approval_payment_term.py @@ -171,7 +171,8 @@ class ApprovalPaymentTerm(models.Model): 'blocking_stage': self.blocking_stage, 'warning_stage': self.warning_stage, 'active_limit': self.active_limit, - 'property_payment_term_id': self.property_payment_term_id.id + 'property_payment_term_id': self.property_payment_term_id.id, + 'is_locked_cbd': False, }) self.approve_date = datetime.utcnow() self.state = 'approved' diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 148a3fd0..017be730 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -1,6 +1,6 @@ from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError -from datetime import datetime +from datetime import datetime, timedelta from odoo.http import request import re import requests @@ -181,10 +181,15 @@ class ResPartner(models.Model): 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') - # no compute - # payment_diff = fields.Selection([('bermasalah', 'Bermasalah'),('sulit', 'Sulit'),('agak_sulit', 'Agak Sulit'),('normal', 'Normal')], string='Payment Difficulty', tracking=3) - - # tidak terpakai + 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.") + + # centang manual is_cbd_locked jika payment term diubah ke CBD + @api.onchange('is_cbd_locked') + def _onchange_is_cbd_locked(self): + if self.is_cbd_locked: + cbd_term = self.env['account.payment.term'].browse(26) + if cbd_term: + self.property_payment_term_id = cbd_term.id @api.model def _default_payment_term(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index ffb53dce..992c1a5d 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -394,6 +394,15 @@ class SaleOrder(models.Model): ('paid', 'Full Paid'), ('no_invoice', 'No Invoice'), ], string="Payment Status Invoice", compute="_compute_payment_state_custom", store=False) + partner_is_cbd_locked = fields.Boolean( + string="Partner Locked CBD", + compute="_compute_partner_is_cbd_locked" + ) + + @api.depends('partner_id.is_cbd_locked') + def _compute_partner_is_cbd_locked(self): + for order in self: + order.partner_is_cbd_locked = order.partner_id.is_cbd_locked @api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual') def _compute_payment_state_custom(self): diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index ca1a36de..7541df35 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -21,6 +21,7 @@ + diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index a1a5e0cd..29d1a980 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -42,6 +42,15 @@ class="btn-primary" attrs="{'invisible': ['|', ('state', 'not in', ['sale', 'done']), ('has_refund', '=', True)]}" /> --> + + + +
-- cgit v1.2.3 From 71f13fcc278b01133d07d56103bbd45afd967d48 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 2 Sep 2025 13:43:43 +0700 Subject: fix finish Checkout --- indoteknik_api/controllers/api_v1/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 374b49a2..65894d8a 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -87,8 +87,8 @@ class SaleOrder(controller.Controller): 'amount_tax': sale.amount_tax, 'amount_total': sale.amount_total, 'expected_ready_to_ship': f"{sale.expected_ready_to_ship.day} {INDONESIAN_MONTHS[sale.expected_ready_to_ship.month]} {sale.expected_ready_to_ship.year}", - 'eta_date_start': f"{sale.eta_date_start.day} {INDONESIAN_MONTHS[sale.eta_date_start.month]} {sale.eta_date_start.year}", - 'eta_date_end': f"{sale.eta_date.day} {INDONESIAN_MONTHS[sale.eta_date.month]} {sale.eta_date.year}", + # 'eta_date_start': f"{sale.eta_date_start.day} {INDONESIAN_MONTHS[sale.eta_date_start.month]} {sale.eta_date_start.year}", + # 'eta_date_end': f"{sale.eta_date.day} {INDONESIAN_MONTHS[sale.eta_date.month]} {sale.eta_date.year}", 'product_name': product_name, 'product_not_in_id': product_not_in_id, 'details': [request.env['sale.order.line'].api_single_response(x, context='with_detail') for x in sale.order_line] -- cgit v1.2.3 From 0d0e2b9a448f6f96a7ab06ecb970dbbd2018f5fe Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 2 Sep 2025 14:19:33 +0700 Subject: (andri) fix layout dan revisi field yang dibutuhkan --- indoteknik_custom/models/unpaid_invoice_view.py | 11 +++---- indoteknik_custom/views/unpaid_invoice_view.xml | 39 +++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/unpaid_invoice_view.py b/indoteknik_custom/models/unpaid_invoice_view.py index 517d078b..f6ce79b8 100644 --- a/indoteknik_custom/models/unpaid_invoice_view.py +++ b/indoteknik_custom/models/unpaid_invoice_view.py @@ -9,21 +9,22 @@ class UnpaidInvoiceView(models.Model): partner_id = fields.Many2one('res.partner', string='Partner') partner_name = fields.Char(string='Partner Name') - email = fields.Char() - phone = fields.Char() + # email = fields.Char() + # phone = fields.Char() invoice_id = fields.Many2one('account.move', string='Invoice') invoice_number = fields.Char(string='Invoice Number') invoice_date = fields.Date() invoice_date_due = fields.Date(string='Due Date') + date_terima_tukar_faktur = fields.Date(string='Terima Faktur') currency_id = fields.Many2one('res.currency', string='Currency') - amount_total = fields.Monetary(string='Total', currency_field='currency_id') - amount_residual = fields.Monetary(string='Residual', currency_field='currency_id') + amount_total = fields.Monetary(string='Total Amount', currency_field='currency_id') + amount_residual = fields.Monetary(string='Sisa Amount', currency_field='currency_id') payment_state = fields.Selection([ ('not_paid','Not Paid'), ('in_payment','In Payment'), ('paid','Paid'), ('reversed','Reversed') ], string='Payment State') - payment_term = fields.Char() + payment_term_id = fields.Many2one('account.payment.term', string='Payment Term') invoice_day_to_due = fields.Integer(string="Day to Due") new_invoice_day_to_due = fields.Integer(string="New Day Due") diff --git a/indoteknik_custom/views/unpaid_invoice_view.xml b/indoteknik_custom/views/unpaid_invoice_view.xml index 19cc07a6..0bbba60a 100644 --- a/indoteknik_custom/views/unpaid_invoice_view.xml +++ b/indoteknik_custom/views/unpaid_invoice_view.xml @@ -8,7 +8,9 @@ - + + + @@ -17,17 +19,48 @@ decoration-danger="payment_state == 'not_paid'" decoration-warning="payment_state in ('partial', 'in_payment')" decoration-success="payment_state in ('paid', 'reversed')"/> - + + + unpaid.invoice.view.form + unpaid.invoice.view + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ Unpaid Invoices Monitoring unpaid.invoice.view tree,form - {} + -- cgit v1.2.3 From ddaedc0eeaf53b47786a7747664fc99ae4dc1a7a Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 2 Sep 2025 14:46:57 +0700 Subject: fix refund validasi --- indoteknik_custom/models/refund_sale_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 737419e7..731d8e41 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -408,6 +408,8 @@ class RefundSaleOrder(models.Model): else: remaining = uang_masuk - amount_refund + if remaining < 0: + raise ValidationError("Semua dana sudah dikembalikan, tidak bisa mengajukan refund") vals['remaining_refundable'] = remaining -- cgit v1.2.3 From 461020919518ac0bde4024de482303734fd314dc Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 2 Sep 2025 14:51:29 +0700 Subject: Fix cancel invoice ccm --- indoteknik_custom/models/tukar_guling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d2e00781..6e839bf0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -134,7 +134,7 @@ class TukarGuling(models.Model): if not self.val_inv_opt and self.is_has_invoice == True: raise UserError("Kalau sudah ada invoice Return Invoice Option harus diisi!") for rec in self: - if rec.val_inv_opt == 'cancel_invoice' and self.is_has_invoice == True: + if rec.val_inv_opt == 'cancel_invoice' and self.is_has_invoice == True and rec.invoice_id.state != 'cancel': raise UserError("Tidak bisa mengubah Return karena sudah ada invoice dan belum di cancel.") elif rec.val_inv_opt == 'tanpa_cancel' and self.is_has_invoice == True: continue -- cgit v1.2.3 From a35dcc5907500e5189516459160c972bb72e1686 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 2 Sep 2025 15:04:33 +0700 Subject: (andri) menambahkan field yang dibutuhkan --- indoteknik_custom/models/unpaid_invoice_view.py | 15 ++++++++++----- indoteknik_custom/views/unpaid_invoice_view.xml | 13 +++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/unpaid_invoice_view.py b/indoteknik_custom/models/unpaid_invoice_view.py index f6ce79b8..f35261eb 100644 --- a/indoteknik_custom/models/unpaid_invoice_view.py +++ b/indoteknik_custom/models/unpaid_invoice_view.py @@ -20,11 +20,16 @@ class UnpaidInvoiceView(models.Model): amount_total = fields.Monetary(string='Total Amount', currency_field='currency_id') amount_residual = fields.Monetary(string='Sisa Amount', currency_field='currency_id') payment_state = fields.Selection([ - ('not_paid','Not Paid'), - ('in_payment','In Payment'), - ('paid','Paid'), - ('reversed','Reversed') - ], string='Payment State') + ('not_paid', 'Not Paid'), + ('in_payment', 'In Payment'), + ('paid', 'Paid'), + ('partial', 'Partially Paid'), + ('reversed', 'Reversed')], string='Payment State') payment_term_id = fields.Many2one('account.payment.term', string='Payment Term') invoice_day_to_due = fields.Integer(string="Day to Due") new_invoice_day_to_due = fields.Integer(string="New Day Due") + + ref = fields.Char(string='Reference') + invoice_user_id = fields.Many2one('res.users', string='Salesperson') + date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur') + sale_id = fields.Many2one('sale.order', string='Sale Order') diff --git a/indoteknik_custom/views/unpaid_invoice_view.xml b/indoteknik_custom/views/unpaid_invoice_view.xml index 0bbba60a..e898dd39 100644 --- a/indoteknik_custom/views/unpaid_invoice_view.xml +++ b/indoteknik_custom/views/unpaid_invoice_view.xml @@ -7,7 +7,9 @@ + + @@ -17,8 +19,8 @@ + decoration-warning="payment_state == 'partial'"/> +
@@ -34,18 +36,21 @@ + + + + + decoration-warning="payment_state == 'partial'"/> -- cgit v1.2.3 From d43eb7ff8ffd4b11120a7354295e86736135344a Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 2 Sep 2025 17:35:47 +0700 Subject: (andri) add menu surat piutang --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 3 +- indoteknik_custom/models/letter_receivable.py | 90 ++++++++++++++++++++++++++ indoteknik_custom/security/ir.model.access.csv | 4 +- indoteknik_custom/views/ir_sequence.xml | 9 +++ indoteknik_custom/views/letter_receivable.xml | 79 ++++++++++++++++++++++ 6 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 indoteknik_custom/models/letter_receivable.py create mode 100644 indoteknik_custom/views/letter_receivable.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 9ebab15e..e300670a 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -178,6 +178,7 @@ # 'views/refund_sale_order.xml', 'views/update_date_planned_po_wizard_view.xml', 'views/unpaid_invoice_view.xml', + 'views/letter_receivable.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index ba3dbad9..c8910669 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -157,4 +157,5 @@ from . import refund_sale_order from . import tukar_guling from . import tukar_guling_po from . import update_date_planned_po_wizard -from . import unpaid_invoice_view \ No newline at end of file +from . import unpaid_invoice_view +from . import letter_receivable \ No newline at end of file diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py new file mode 100644 index 00000000..541f64ff --- /dev/null +++ b/indoteknik_custom/models/letter_receivable.py @@ -0,0 +1,90 @@ +from odoo import models, fields, api + +class SuratPiutang(models.Model): + _name = "surat.piutang" + _description = "Surat Piutang" + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string="Nomor Surat", readonly=True, copy=False) + partner_id = fields.Many2one("res.partner", string="Customer", required=True) + tujuan_nama = fields.Char(string="Nama Tujuan") + tujuan_email = fields.Char(string="Email Tujuan") + perihal = fields.Selection([ + ('sp1', 'Surat Peringatan Piutang'), + ('sp2', 'Surat Peringatan Piutang ke-2'), + ('sp3', 'Surat Peringatan Piutang ke-3') + ], string="Perihal", required=True, tracking=True) + line_ids = fields.One2many("surat.piutang.line", "surat_id", string="Invoice Lines") + state = fields.Selection([ + ("draft", "Draft"), + ("sent", "Sent") + ], default="draft", tracking=True) + send_date = fields.Datetime(string="Tanggal Kirim", tracking=True) + + currency_id = fields.Many2one('res.currency') + + # Grand total (total sisa semua line yang dicentang) + grand_total = fields.Monetary( + string='Total Sisa', + currency_field='currency_id', + compute='_compute_grand_total', + ) + + @api.depends('line_ids.amount_residual', 'line_ids.selected') + def _compute_grand_total(self): + for rec in self: + rec.grand_total = sum( + line.amount_residual or 0.0 for line in rec.line_ids if line.selected + ) + + @api.onchange('partner_id') + def _onchange_partner_id(self): + if self.partner_id: + invoice_lines = self.env['unpaid.invoice.view'].search( + [('partner_id', '=', self.partner_id.id)], + order='new_invoice_day_to_due asc' + ) + lines = [(0, 0, { + 'invoice_view_id': inv.id, + 'invoice_id': inv.invoice_id.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'selected': False + }) for inv in invoice_lines] + self.line_ids = lines + + @api.model + def create(self, vals): + # Generate nomor surat otomatis + if not vals.get("name"): + seq = self.env["ir.sequence"].next_by_code("surat.piutang") or "000" + today = fields.Date.today() + bulan_romawi = ["I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII"][today.month-1] + tahun = today.strftime("%y") + vals["name"] = f"{seq}/LO/FAT/IDG/{bulan_romawi}/{tahun}" + + return super().create(vals) + +class SuratPiutangLine(models.Model): + _name = 'surat.piutang.line' + _description = 'Surat Piutang Line' + + surat_id = fields.Many2one('surat.piutang', string='Surat Piutang', ondelete='cascade') + invoice_view_id = fields.Many2one('unpaid.invoice.view', string='Unpaid Invoice') + invoice_id = fields.Many2one('account.move', string='Invoice') + selected = fields.Boolean(string="Pilih", default=False) + + invoice_number = fields.Char(string='Invoice Number') + invoice_date = fields.Date(string='Invoice Date') + invoice_date_due = fields.Date(string='Due Date') + invoice_day_to_due = fields.Integer(string='Day to Due') + ref = fields.Char(string='Reference') + amount_residual = fields.Monetary(string='Amount Due Signed') + currency_id = fields.Many2one('res.currency') + payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms') diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4649c743..25273893 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -196,4 +196,6 @@ access_purchase_order_update_date_wizard,access.purchase.order.update.date.wizar access_sync_promise_date_wizard,access.sync.promise.date.wizard,model_sync_promise_date_wizard,base.group_user,1,1,1,1 access_sync_promise_date_wizard_line,access.sync.promise.date.wizard.line,model_sync_promise_date_wizard_line,base.group_user,1,1,1,1 access_change_date_planned_wizard,access.change.date.planned.wizard,model_change_date_planned_wizard,,1,1,1,1 -access_unpaid_invoice_view,access.unpaid.invoice.view,model_unpaid_invoice_view,base.group_user,1,1,1,1 \ No newline at end of file +access_unpaid_invoice_view,access.unpaid.invoice.view,model_unpaid_invoice_view,base.group_user,1,1,1,1 +access_surat_piutang_user,surat.piutang user,model_surat_piutang,base.group_user,1,1,1,1 +access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 94c2cd07..4b8fec53 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -228,5 +228,14 @@ 1 True + + + Surat Piutang + surat.piutang + + 3 + 1 + 1 + \ No newline at end of file diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml new file mode 100644 index 00000000..72eae640 --- /dev/null +++ b/indoteknik_custom/views/letter_receivable.xml @@ -0,0 +1,79 @@ + + + + surat.piutang.tree + surat.piutang + + + + + + + + + + + + + + + surat.piutang.form + surat.piutang + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + Surat Piutang + + + + + + Surat Piutang + surat.piutang + tree,form + + + + +
-- cgit v1.2.3 From 1c71710dbf42106a82c0a8e30ec9cee7f452a387 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 3 Sep 2025 11:07:40 +0700 Subject: (andri) add refresh invoices agar data selalu update & fix layout form --- indoteknik_custom/models/letter_receivable.py | 61 ++++++++++++++++++++++++++- indoteknik_custom/views/letter_receivable.xml | 44 ++++++++++++++++--- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index 541f64ff..10198fbf 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -4,19 +4,22 @@ class SuratPiutang(models.Model): _name = "surat.piutang" _description = "Surat Piutang" _inherit = ['mail.thread', 'mail.activity.mixin'] + _order = 'name desc' name = fields.Char(string="Nomor Surat", readonly=True, copy=False) partner_id = fields.Many2one("res.partner", string="Customer", required=True) tujuan_nama = fields.Char(string="Nama Tujuan") tujuan_email = fields.Char(string="Email Tujuan") perihal = fields.Selection([ - ('sp1', 'Surat Peringatan Piutang'), + ('penagihan', 'Surat Resmi Penagihan'), + ('sp1', 'Surat Peringatan Piutang ke-1'), ('sp2', 'Surat Peringatan Piutang ke-2'), ('sp3', 'Surat Peringatan Piutang ke-3') ], string="Perihal", required=True, tracking=True) line_ids = fields.One2many("surat.piutang.line", "surat_id", string="Invoice Lines") state = fields.Selection([ ("draft", "Draft"), + ("approval_pimpinan", "Menunggu Approval Pimpinan"), ("sent", "Sent") ], default="draft", tracking=True) send_date = fields.Datetime(string="Tanggal Kirim", tracking=True) @@ -59,6 +62,62 @@ class SuratPiutang(models.Model): }) for inv in invoice_lines] self.line_ids = lines + def action_refresh_lines(self): + for rec in self: + if not rec.partner_id: + continue + + # Ambil semua unpaid terbaru + invoice_views = self.env['unpaid.invoice.view'].search( + [('partner_id', '=', rec.partner_id.id)], + order='new_invoice_day_to_due asc' + ) + + existing_lines = {line.invoice_id.id: line for line in rec.line_ids} + + # Cache selected status per invoice id + selected_map = {line.invoice_id.id: line.selected for line in rec.line_ids} + + # Invoice id yang masih ada di unpaid + new_invoice_ids = invoice_views.mapped('invoice_id.id') + + for inv in invoice_views: + if inv.invoice_id.id in existing_lines: + # update line lama + line = existing_lines[inv.invoice_id.id] + line.write({ + 'invoice_view_id': inv.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'selected': selected_map.get(inv.invoice_id.id, line.selected), + }) + else: + # preserve selected kalau pernah ada di cache + self.env['surat.piutang.line'].create({ + 'surat_id': rec.id, + 'invoice_view_id': inv.id, + 'invoice_id': inv.invoice_id.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'selected': selected_map.get(inv.invoice_id.id, False), + }) + + # Hapus line yang tidak ada lagi di unpaid view + rec.line_ids.filtered(lambda l: l.invoice_id.id not in new_invoice_ids).unlink() + + @api.model def create(self, vals): # Generate nomor surat otomatis diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 72eae640..fe60eb39 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -8,9 +8,12 @@ - + - + @@ -22,19 +25,48 @@
- +
+ + + +
+

+ +

+
- - - + + + + + + + @@ -105,7 +112,8 @@ - + @@ -133,7 +141,7 @@ 1 - + @@ -141,7 +149,8 @@ - + + - - + + - - + + - + - - + + - + @@ -220,7 +235,8 @@ - - -- cgit v1.2.3 From f82137850801cb8d4c8cb482f607b68181ef1cb6 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 6 Sep 2025 08:33:49 +0700 Subject: carte --- indoteknik_custom/models/sj_tele.py | 27 ++++++++++++++++++++++++++- indoteknik_custom/views/sj_tele.xml | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index 246c0f43..c8f7c0c6 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -1,7 +1,8 @@ from odoo import models, fields, api +from odoo.exceptions import UserError import requests import json -import logging +import logging, subprocess _logger = logging.getLogger(__name__) @@ -16,6 +17,30 @@ class SjTele(models.Model): sale_name = fields.Char(string='Sale Name') create_date = fields.Datetime(string='Create Date') + @api.model + def run_pentaho_carte(self): + carte = "http://127.0.0.1:8080" + job_kjb = r"C:/Users/ThinkPad/Desktop/tes.kjb" + params = {"job": job_kjb, "level": "Basic", "block": "Y"} + try: + r = requests.get( + f"{carte}/kettle/executeJob/", + params=params, + auth=("cluster", "cluster"), + timeout=900, + ) + r.raise_for_status() + # kalau Carte mengembalikan ERROR, anggap gagal + if "ERROR" in r.text: + raise UserError(f"Carte error: {r.text}") + except Exception as e: + _logger.exception("Carte call failed: %s", e) + raise UserError(f"Gagal memanggil Carte: {e}") + + self.env['sj.tele'].sudo().woi() + + return True + def woi(self): bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' chat_id_mqdd = '-4885333032' diff --git a/indoteknik_custom/views/sj_tele.xml b/indoteknik_custom/views/sj_tele.xml index cefcc968..2e1e5120 100644 --- a/indoteknik_custom/views/sj_tele.xml +++ b/indoteknik_custom/views/sj_tele.xml @@ -4,7 +4,7 @@ SJ TELE code - model.woi() + model.run_pentaho_carte() 1 days -1 -- cgit v1.2.3 From 4bd34e21d8d4dc3b2f688e181fb05c6359ac1f43 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 6 Sep 2025 11:29:08 +0700 Subject: fix manufacture in stock move line & done --- indoteknik_custom/models/stock_move.py | 3 +-- indoteknik_custom/views/stock_move_line.xml | 9 +++++---- indoteknik_custom/views/stock_picking.xml | 7 ++++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 9ec36fcd..24d405a6 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -18,8 +18,7 @@ class StockMove(models.Model): 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) - product_image= fields.Image('Product Image', related='product_id.image_1920') - + product_image = fields.Binary(related="product_id.image_128", string="Product Image", readonly = True) # @api.model_create_multi # def create(self, vals_list): # moves = super(StockMove, self).create(vals_list) diff --git a/indoteknik_custom/views/stock_move_line.xml b/indoteknik_custom/views/stock_move_line.xml index 757d2522..94c0bf53 100644 --- a/indoteknik_custom/views/stock_move_line.xml +++ b/indoteknik_custom/views/stock_move_line.xml @@ -3,18 +3,19 @@ Stock Move Line stock.move.line - + + - + - + Stock Move Line stock.move.line - + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index c8aa6454..50c5f261 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -99,7 +99,7 @@ attrs="{'invisible': [('state_approve_md', 'not in', ['waiting'])]}" /> - @@ -140,6 +140,11 @@ 1 + + + + -- cgit v1.2.3 From ea6364616f5a6ecbcf933249aeb95a7f3c8b4555 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 6 Sep 2025 11:33:02 +0700 Subject: Comment --- indoteknik_custom/models/sj_tele.py | 45 ++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index c8f7c0c6..c00279ec 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -3,6 +3,7 @@ from odoo.exceptions import UserError import requests import json import logging, subprocess +import time _logger = logging.getLogger(__name__) @@ -17,29 +18,31 @@ class SjTele(models.Model): sale_name = fields.Char(string='Sale Name') create_date = fields.Datetime(string='Create Date') - @api.model - def run_pentaho_carte(self): - carte = "http://127.0.0.1:8080" - job_kjb = r"C:/Users/ThinkPad/Desktop/tes.kjb" - params = {"job": job_kjb, "level": "Basic", "block": "Y"} - try: - r = requests.get( - f"{carte}/kettle/executeJob/", - params=params, - auth=("cluster", "cluster"), - timeout=900, - ) - r.raise_for_status() - # kalau Carte mengembalikan ERROR, anggap gagal - if "ERROR" in r.text: - raise UserError(f"Carte error: {r.text}") - except Exception as e: - _logger.exception("Carte call failed: %s", e) - raise UserError(f"Gagal memanggil Carte: {e}") + # @api.model + # def run_pentaho_carte(self): + # carte = "http://127.0.0.1:8080" + # job_kjb = r"C:/Users/Indoteknik/Desktop/tes.kjb" + # params = {"job": job_kjb, "level": "Basic", "block": "Y"} + # try: + # r = requests.get( + # f"{carte}/kettle/executeJob/", + # params=params, + # auth=("cluster", "cluster"), + # timeout=900, + # ) + # r.raise_for_status() + # # kalau Carte mengembalikan ERROR, anggap gagal + # if "ERROR" in r.text: + # raise UserError(f"Carte error: {r.text}") + # except Exception as e: + # _logger.exception("Carte call failed: %s", e) + # raise UserError(f"Gagal memanggil Carte: {e}") + + # time.sleep(3) - self.env['sj.tele'].sudo().woi() + # self.env['sj.tele'].sudo().woi() - return True + # return True def woi(self): bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' -- cgit v1.2.3 From 76676d8b0c41672d11ae392500dfb5a6db1cfd91 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 6 Sep 2025 11:33:57 +0700 Subject: fix called method --- indoteknik_custom/views/sj_tele.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/sj_tele.xml b/indoteknik_custom/views/sj_tele.xml index 2e1e5120..cefcc968 100644 --- a/indoteknik_custom/views/sj_tele.xml +++ b/indoteknik_custom/views/sj_tele.xml @@ -4,7 +4,7 @@ SJ TELE code - model.run_pentaho_carte() + model.woi() 1 days -1 -- cgit v1.2.3 From 4cdb3a7b89d5e93449fadafec015e80dad1cf47f Mon Sep 17 00:00:00 2001 From: AndriFP Date: Sat, 6 Sep 2025 14:04:27 +0700 Subject: (andri) add validasi di SO dan respartner --- indoteknik_custom/models/res_partner.py | 16 +++++++++------- indoteknik_custom/models/sale_order.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 80fe643b..b5ce9266 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -183,13 +183,6 @@ class ResPartner(models.Model): 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.") - # centang manual is_cbd_locked jika payment term diubah ke CBD - # @api.onchange('is_cbd_locked') - # def _onchange_is_cbd_locked(self): - # if self.is_cbd_locked: - # cbd_term = self.env['account.payment.term'].browse(26) - # if cbd_term: - # self.property_payment_term_id = cbd_term.id @api.model def _default_payment_term(self): @@ -201,6 +194,15 @@ class ResPartner(models.Model): default=_default_payment_term, tracking=3 ) + @api.constrains('property_payment_term_id', 'is_cbd_locked') + def _check_cbd_lock_partner(self): + cbd_term = self.env['account.payment.term'].browse(26) + for rec in self: + if rec.is_cbd_locked and rec.property_payment_term_id and rec.property_payment_term_id != cbd_term: + raise ValidationError( + "Partner ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." + ) + @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id", "kecamatan_id") def _alamat_lengkap_text(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 992c1a5d..8a595d8e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -404,6 +404,23 @@ class SaleOrder(models.Model): for order in self: order.partner_is_cbd_locked = order.partner_id.is_cbd_locked + @api.onchange('payment_term_id') + def _onchange_partner_payment_term(self): + cbd_term = self.env['account.payment.term'].browse(26) + for rec in self: + if rec.partner_id and rec.partner_id.is_cbd_locked and cbd_term: + rec.payment_term_id = cbd_term + + @api.constrains('payment_term_id', 'partner_id', 'state') + def _check_cbd_lock_sale_order(self): + cbd_term = self.env['account.payment.term'].browse(26) + for rec in self: + if rec.state == 'draft' and rec.partner_id.is_cbd_locked: + if rec.payment_term_id and rec.payment_term_id != cbd_term: + raise ValidationError( + "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." + ) + @api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual') def _compute_payment_state_custom(self): for order in self: -- cgit v1.2.3 From 943394bf2a90931134317b88615f23900664dcf0 Mon Sep 17 00:00:00 2001 From: AndriFP Date: Sun, 7 Sep 2025 20:18:18 +0700 Subject: (andri) fix --- indoteknik_custom/models/letter_receivable.py | 10 ++++------ indoteknik_custom/views/letter_receivable.xml | 5 +++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index 4159ecc2..5a793873 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -16,9 +16,9 @@ class SuratPiutang(models.Model): _order = 'name desc' name = fields.Char(string="Nomor Surat", readonly=True, copy=False) - partner_id = fields.Many2one("res.partner", string="Customer", required=True) - tujuan_nama = fields.Char(string="Nama Tujuan") - tujuan_email = fields.Char(string="Email Tujuan") + partner_id = fields.Many2one("res.partner", string="Customer", required=True, tracking=True) + tujuan_nama = fields.Char(string="Nama Tujuan", tracking=True) + tujuan_email = fields.Char(string="Email Tujuan", tracking=True) perihal = fields.Selection([ ('penagihan', 'Surat Resmi Penagihan'), ('sp1', 'Surat Peringatan Piutang ke-1'), @@ -123,9 +123,6 @@ class SuratPiutang(models.Model): invoice_table_rows = "" grand_total = 0 for line in selected_lines: - inv = line.invoice_id - if not inv: - continue days_to_due = (line.invoice_date_due - today).days if line.invoice_date_due else 0 grand_total += line.amount_residual invoice_table_rows += f""" @@ -279,6 +276,7 @@ class SuratPiutangLine(models.Model): invoice_number = fields.Char(string='Invoice Number') invoice_date = fields.Date(string='Invoice Date') invoice_date_due = fields.Date(string='Due Date') + invoice_new_day_to_due = fields.Integer(string='New Day to Due') invoice_day_to_due = fields.Integer(string='Day to Due') ref = fields.Char(string='Reference') amount_residual = fields.Monetary(string='Amount Due Signed') diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index e5acaef4..055ea125 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -56,8 +56,8 @@ - + @@ -84,13 +84,14 @@ + - + -- cgit v1.2.3 From 15b5f549777065dac76eaaa23598f09ae6a22f8f Mon Sep 17 00:00:00 2001 From: AndriFP Date: Mon, 8 Sep 2025 08:54:28 +0700 Subject: (andri) fix --- indoteknik_custom/models/letter_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index 5a793873..d4921bc5 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -187,6 +187,7 @@ class SuratPiutang(models.Model): 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, @@ -224,6 +225,7 @@ class SuratPiutang(models.Model): 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, @@ -240,6 +242,7 @@ class SuratPiutang(models.Model): 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, @@ -276,8 +279,8 @@ class SuratPiutangLine(models.Model): invoice_number = fields.Char(string='Invoice Number') invoice_date = fields.Date(string='Invoice Date') invoice_date_due = fields.Date(string='Due Date') - invoice_new_day_to_due = fields.Integer(string='New Day to Due') invoice_day_to_due = fields.Integer(string='Day to Due') + new_invoice_day_to_due = fields.Integer(string='New Day to Due') ref = fields.Char(string='Reference') amount_residual = fields.Monetary(string='Amount Due Signed') currency_id = fields.Many2one('res.currency') -- cgit v1.2.3 From 2b14a2678e3e2782c362065032cfcaba5b091b88 Mon Sep 17 00:00:00 2001 From: AndriFP Date: Mon, 8 Sep 2025 11:13:21 +0700 Subject: (andri) add compute char periode invoices --- indoteknik_custom/models/letter_receivable.py | 64 ++++++++++++++++++++++++--- indoteknik_custom/views/letter_receivable.xml | 12 +++-- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index d4921bc5..caf62a64 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -6,6 +6,7 @@ from terbilang import Terbilang import re import logging from datetime import timedelta +import babel _logger = logging.getLogger(__name__) @@ -33,6 +34,10 @@ class SuratPiutang(models.Model): ], default="draft", tracking=True) send_date = fields.Datetime(string="Tanggal Kirim", tracking=True) seven_days_after_sent_date = fields.Char(string="7 Hari Setelah Tanggal Kirim", tracking=True) + periode_invoices_terpilih = fields.Char( + string="Periode Invoices Terpilih", + compute="_compute_periode_invoices", + ) currency_id = fields.Many2one('res.currency') @@ -48,6 +53,34 @@ class SuratPiutang(models.Model): compute="_compute_grand_total_text", ) + @api.depends("line_ids.selected", "line_ids.invoice_date") + def _compute_periode_invoices(self): + for rec in self: + selected_lines = rec.line_ids.filtered(lambda l: l.selected and l.invoice_date) + if not selected_lines: + rec.periode_invoices_terpilih = "-" + continue + + dates = selected_lines.mapped("invoice_date") + min_date, max_date = min(dates), max(dates) + + # Ambil bagian bulan & tahun + min_month = babel.dates.format_date(min_date, "MMMM", locale="id_ID") + min_year = min_date.year + max_month = babel.dates.format_date(max_date, "MMMM", locale="id_ID") + max_year = max_date.year + + if min_year == max_year: + if min_month == max_month: + # example: Januari 2025 + rec.periode_invoices_terpilih = f"{min_month} {min_year}" + else: + # example: Mei s/d Juni 2025 + rec.periode_invoices_terpilih = f"{min_month} s/d {max_month} {max_year}" + else: + # example: Desember 2024 s/d Januari 2025 + rec.periode_invoices_terpilih = f"{min_month} {min_year} s/d {max_month} {max_year}" + def _compute_grand_total_text(self): tb = Terbilang() for record in self: @@ -181,17 +214,21 @@ class SuratPiutang(models.Model): order='new_invoice_day_to_due asc' ) lines = [(0, 0, { - 'invoice_view_id': inv.id, + # 'invoice_view_id': inv.id, 'invoice_id': inv.invoice_id.id, 'invoice_number': inv.invoice_number, 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, - 'new_invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, 'selected': False }) for inv in invoice_lines] self.line_ids = lines @@ -220,33 +257,41 @@ class SuratPiutang(models.Model): # update line lama line = existing_lines[inv.invoice_id.id] line.write({ - 'invoice_view_id': inv.id, + # 'invoice_view_id': inv.id, 'invoice_number': inv.invoice_number, 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, - 'new_invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, 'selected': selected_map.get(inv.invoice_id.id, line.selected), }) else: # preserve selected kalau pernah ada di cache self.env['surat.piutang.line'].create({ 'surat_id': rec.id, - 'invoice_view_id': inv.id, + # 'invoice_view_id': inv.id, 'invoice_id': inv.invoice_id.id, 'invoice_number': inv.invoice_number, 'invoice_date': inv.invoice_date, 'invoice_date_due': inv.invoice_date_due, 'invoice_day_to_due': inv.invoice_day_to_due, - 'new_invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, 'ref': inv.ref, 'amount_residual': inv.amount_residual, 'currency_id': inv.currency_id.id, 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, 'selected': selected_map.get(inv.invoice_id.id, False), }) @@ -272,7 +317,7 @@ class SuratPiutangLine(models.Model): _description = 'Surat Piutang Line' surat_id = fields.Many2one('surat.piutang', string='Surat Piutang', ondelete='cascade') - invoice_view_id = fields.Many2one('unpaid.invoice.view', string='Unpaid Invoice') + # invoice_view_id = fields.Many2one('unpaid.invoice.view', string='Unpaid Invoice') invoice_id = fields.Many2one('account.move', string='Invoice') selected = fields.Boolean(string="Pilih", default=False) @@ -285,3 +330,8 @@ class SuratPiutangLine(models.Model): amount_residual = fields.Monetary(string='Amount Due Signed') currency_id = fields.Many2one('res.currency') payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms') + + date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur') + date_terima_tukar_faktur = fields.Date(string='Terima Faktur') + invoice_user_id = fields.Many2one('res.users', string='Salesperson') + sale_id = fields.Many2one('sale.order', string='Sale Order') diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 055ea125..24c0dae1 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -80,19 +80,25 @@ ) +
- + + - - + + + + + + -- cgit v1.2.3 From 404f4da511b131c4b7714d00e65a856db6d08591 Mon Sep 17 00:00:00 2001 From: AndriFP Date: Mon, 8 Sep 2025 11:20:05 +0700 Subject: (andri) fix --- indoteknik_custom/views/letter_receivable.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 24c0dae1..3639452c 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -80,7 +80,11 @@ ) - + +
+ Periode Invoices Terpilih: + +
-- cgit v1.2.3 From f2adf9ad9ed7491a6ad5422d1236761e7d42c82f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 8 Sep 2025 12:30:59 +0700 Subject: remove image in tree view --- indoteknik_custom/views/stock_picking.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 50c5f261..fc8be790 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -99,11 +99,11 @@ attrs="{'invisible': [('state_approve_md', 'not in', ['waiting'])]}" /> - - + --> -- cgit v1.2.3 From 6188090965c5381fa120268a024a486e62056505 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 8 Sep 2025 15:05:19 +0700 Subject: hilangin validasi sementara --- indoteknik_custom/models/tukar_guling_po.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index f2f37606..c65ac286 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -441,8 +441,8 @@ class TukarGulingPO(models.Model): ('state', '!=', 'cancel'), ], limit=1) - if existing_tukar_guling: - raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + # if existing_tukar_guling: + # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations pick_id = self.operations.picking_type_id.id -- cgit v1.2.3 From 7a34ad0965e08d1428c700f09a49f30f17c1996c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 8 Sep 2025 15:08:54 +0700 Subject: Balikin --- indoteknik_custom/models/tukar_guling_po.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index c65ac286..f2f37606 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -441,8 +441,8 @@ class TukarGulingPO(models.Model): ('state', '!=', 'cancel'), ], limit=1) - # if existing_tukar_guling: - # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + if existing_tukar_guling: + raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations pick_id = self.operations.picking_type_id.id -- cgit v1.2.3 From 69c9579636182d81b1b6da010f29f5efda6f88a7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 8 Sep 2025 15:13:38 +0700 Subject: Fix oauth google --- indoteknik_api/controllers/api_v1/user.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py index dde30fec..c75e4954 100644 --- a/indoteknik_api/controllers/api_v1/user.py +++ b/indoteknik_api/controllers/api_v1/user.py @@ -89,7 +89,8 @@ class User(controller.Controller): 'name': name, 'login': email, 'oauth_provider_id': request.env.ref('auth_oauth.provider_google').id, - 'sel_groups_1_9_10': 9 + 'sel_groups_1_9_10': 9, + 'active': True } user = request.env['res.users'].create(user_data) -- cgit v1.2.3 From 03db5f5bfd027fceeb0733bbb51e95e03c43d2cd Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 8 Sep 2025 16:20:28 +0700 Subject: copy false PO refund --- indoteknik_custom/models/purchase_order.py | 2 +- indoteknik_custom/models/refund_sale_order.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 50913a80..18811b85 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -66,7 +66,7 @@ class PurchaseOrder(models.Model): sale_order = fields.Char(string='Sale Order') matches_so = fields.Many2many('sale.order', string='Matches SO', compute='_compute_matches_so') is_create_uangmuka = fields.Boolean(string='Uang Muka?') - move_id = fields.Many2one('account.move', string='Journal Entries Uang Muka', domain=[('move_type', '=', 'entry')]) + move_id = fields.Many2one('account.move', string='Journal Entries Uang Muka', domain=[('move_type', '=', 'entry')], copy=False) logbook_bill_id = fields.Many2one('report.logbook.bill', string='Logbook Bill') status_printed = fields.Selection([ ('not_printed', 'Belum Print'), diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 731d8e41..9ab18f27 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -76,6 +76,7 @@ class RefundSaleOrder(models.Model): transfer_move_id = fields.Many2one( 'account.move', string="Journal Payment", + copy=False, help="Pilih transaksi salah transfer dari jurnal Uang Muka (journal_id=11) yang tidak terkait SO." ) -- cgit v1.2.3 From 6f7fd357434cf8372eeb1495612b74a9e16d22e0 Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Tue, 9 Sep 2025 09:40:01 +0700 Subject: (andri) fix --- indoteknik_custom/models/letter_receivable.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index caf62a64..8722ab8d 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -135,7 +135,7 @@ class SuratPiutang(models.Model): raise UserError(_("Email tujuan harus diisi.")) template = self.env.ref('indoteknik_custom.letter_receivable_mail_template') - today = fields.Date.today() + # today = fields.Date.today() month_map = { 1: "Januari", 2: "Februari", 3: "Maret", 4: "April", @@ -156,7 +156,7 @@ class SuratPiutang(models.Model): invoice_table_rows = "" grand_total = 0 for line in selected_lines: - days_to_due = (line.invoice_date_due - today).days if line.invoice_date_due else 0 + # days_to_due = (line.invoice_date_due - today).days if line.invoice_date_due else 0 grand_total += line.amount_residual invoice_table_rows += f""" @@ -164,7 +164,7 @@ class SuratPiutang(models.Model): {self.partner_id.name or '-'} {fields.Date.to_string(line.invoice_date) or '-'} {fields.Date.to_string(line.invoice_date_due) or '-'} - {days_to_due} + {line.new_invoice_day_to_due} {line.ref or '-'} {formatLang(self.env, line.amount_residual, currency_obj=line.currency_id)} {line.payment_term_id.name or '-'} -- cgit v1.2.3 From 7f49cfa92bed67bea8359433ff5c08cb069f284e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 9 Sep 2025 11:49:54 +0700 Subject: add tanggal kirim sj --- indoteknik_custom/models/sj_tele.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index c00279ec..8864a313 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -4,6 +4,7 @@ import requests import json import logging, subprocess import time +from collections import OrderedDict _logger = logging.getLogger(__name__) @@ -17,6 +18,7 @@ class SjTele(models.Model): picking_name = fields.Char(string='Picking Name') sale_name = fields.Char(string='Sale Name') create_date = fields.Datetime(string='Create Date') + date_doc_kirim = fields.Datetime(string='Tanggal Kirim SJ') # @api.model # def run_pentaho_carte(self): @@ -62,13 +64,32 @@ class SjTele(models.Model): _logger.exception("Gagal kirim Telegram (no data): %s", e) return True + lines = [] + groups = OrderedDict() + for rec in data: name = rec.picking_name or (rec.picking_id.name if rec.picking_id else '') pid = rec.picking_id.id if rec.picking_id else '' - so = rec.sale_id.name or rec.sale_name or '' + so = rec.sale_id.name or rec.sale_name or '' + dttm = (rec.picking_id.date_doc_kirim if (rec.picking_id and rec.picking_id.date_doc_kirim) + else getattr(rec, 'date_doc_kirim', None)) + + # format header tanggal (string), tanpa konversi Waktu/WIB + if dttm: + date_header = dttm if isinstance(dttm, str) else fields.Datetime.to_string(dttm) + date_header = date_header[:10] + else: + date_header = '(Tidak ada tanggal kirim SJ)' + if name: - lines.append(f"{name} - {so} ({pid})") + groups.setdefault(date_header, []).append(f"- ({pid}) - {name} - {so}") + + # build output berurutan per tanggal + for header_date, items in groups.items(): + lines.append(header_date) + lines.extend(items) + header = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\n" text = header + "\n".join(lines) -- cgit v1.2.3 From ac13214fcab7a580b7c9b80faec8cfef684c09aa Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:43:20 +0700 Subject: (andri) try qweb --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/letter_receivable.py | 5 + indoteknik_custom/report/report_surat_piutang.xml | 149 ++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 indoteknik_custom/report/report_surat_piutang.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 493c3ad6..b751b64e 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -166,6 +166,7 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'report/report_surat_piutang.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', 'views/public_holiday.xml', diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index 8722ab8d..9a6c664c 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -53,6 +53,11 @@ class SuratPiutang(models.Model): compute="_compute_grand_total_text", ) + def action_print_surat_piutang(self): + self.ensure_one() + return self.env.ref('indoteknik_custom.report_surat_piutang_formal').report_action(self) + + @api.depends("line_ids.selected", "line_ids.invoice_date") def _compute_periode_invoices(self): for rec in self: diff --git a/indoteknik_custom/report/report_surat_piutang.xml b/indoteknik_custom/report/report_surat_piutang.xml new file mode 100644 index 00000000..cb5762f3 --- /dev/null +++ b/indoteknik_custom/report/report_surat_piutang.xml @@ -0,0 +1,149 @@ + + + + + + + + + + Surat Peringatan Piutang + surat.piutang + qweb-pdf + indoteknik_custom.report_surat_piutang_formal_custom + indoteknik_custom.report_surat_piutang_formal_custom + + report + + + + + + + -- cgit v1.2.3 From 97be5b4f02f5eb499ab6cf2afa031a703d2a5866 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 11:43:40 +0700 Subject: done kyknya --- indoteknik_custom/models/sale_order.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8cb169a6..630f25ba 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1900,10 +1900,10 @@ class SaleOrder(models.Model): # raise UserError('Kelurahan Real Delivery Address harus diisi') 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.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 so_number = self.name so_number = so_number.replace('/', '-') @@ -1916,8 +1916,8 @@ class SaleOrder(models.Model): } # ==== ENV ==== - # check_url = f'https://api.sandbox.midtrans.com/v2/{so_number}/status' # dev - sandbox - check_url = f'https://api.midtrans.com/v2/{so_number}/status' # production + check_url = f'https://api.sandbox.midtrans.com/v2/{so_number}/status' # dev - sandbox + # check_url = f'https://api.midtrans.com/v2/{so_number}/status' # production # ============================================= check_response = requests.get(check_url, headers=headers) @@ -2387,17 +2387,23 @@ class SaleOrder(models.Model): # Ambil blocking stage dari partner block_stage = rec.partner_id.parent_id.blocking_stage if rec.partner_id.parent_id else rec.partner_id.blocking_stage or 0 is_cbd = rec.partner_id.parent_id.property_payment_term_id.id == 26 if rec.partner_id.parent_id else rec.partner_id.property_payment_term_id.id == 26 or False + partner_term = rec.partner_id.property_payment_term_id + partner_term_days_total = 0 + if partner_term: + partner_term_days_total = sum((line.days or 0) for line in partner_term.line_ids) + is_partner_cbd = (partner_term_days_total == 0) + is_so_cbd = bool(rec.payment_term_id.id == 26) # Ambil jumlah nilai dari SO yang invoice_status masih 'to invoice' so_to_invoice = 0 for sale in rec.partner_id.sale_order_ids: if sale.invoice_status == 'to invoice': so_to_invoice = so_to_invoice + sale.amount_total - # Hitung remaining credit limit - remaining_credit_limit = block_stage - current_total - so_to_invoice + + remaining_credit_limit = block_stage - current_total - so_to_invoice if not is_cbd and not is_partner_cbd else 0 # Validasi limit - if remaining_credit_limit <= 0 and block_stage > 0 and not is_cbd: + if remaining_credit_limit <= 0 and block_stage > 0 and not is_cbd and not is_so_cbd and not is_partner_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.") % (rec.partner_id.name, block_stage - current_total, block_stage, outstanding_amount)) -- cgit v1.2.3 From 1767a569e88e8254fdbefdfaf6be6045f1f5235f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 11:45:39 +0700 Subject: apt request done --- indoteknik_custom/models/approval_payment_term.py | 4 ++-- indoteknik_custom/views/approval_payment_term.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/approval_payment_term.py b/indoteknik_custom/models/approval_payment_term.py index 8618856a..85fd1613 100644 --- a/indoteknik_custom/models/approval_payment_term.py +++ b/indoteknik_custom/models/approval_payment_term.py @@ -69,8 +69,8 @@ class ApprovalPaymentTerm(models.Model): return res def _track_changes_for_user_688(self, vals, old_values_dict): - if self.env.user.id != 688: - return + # if self.env.user.id != 688: + # return tracked_fields = {"blocking_stage", "warning_stage", "property_payment_term_id"} diff --git a/indoteknik_custom/views/approval_payment_term.xml b/indoteknik_custom/views/approval_payment_term.xml index 5c130f3f..b0b99689 100644 --- a/indoteknik_custom/views/approval_payment_term.xml +++ b/indoteknik_custom/views/approval_payment_term.xml @@ -7,7 +7,7 @@ - + -- cgit v1.2.3 From e3c9d12ca2356493bb75c7362d0da54280889828 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 12:22:07 +0700 Subject: dunning done --- indoteknik_custom/models/dunning_run.py | 2 ++ indoteknik_custom/views/dunning_run.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 5a6aebac..eb6f06dc 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -31,6 +31,7 @@ class DunningRun(models.Model): description = fields.Char(string='Description') comment = fields.Char(string='Comment') grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") + is_paid = fields.Boolean(string='Sudah Bayar?') def _compute_grand_total(self): for record in self: @@ -149,4 +150,5 @@ class DunningRunLine(models.Model): total_amt = fields.Float(string='Total Amount') open_amt = fields.Float(string='Open Amount') due_date = fields.Date(string='Due Date') + payment_term = fields.Many2one('account.payment.term', related='invoice_id.invoice_payment_term_id', string='Payment Term') diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index f624c42e..7623666b 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -25,6 +25,7 @@ + @@ -74,6 +75,7 @@ + -- cgit v1.2.3 From 32d0ca72aa38701747794dab06e7fb98b6f31489 Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:42:34 +0700 Subject: (andri) rev validasi --- indoteknik_custom/models/res_partner.py | 4 ++-- indoteknik_custom/models/sale_order.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index b5ce9266..0c932dbe 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -196,9 +196,9 @@ class ResPartner(models.Model): @api.constrains('property_payment_term_id', 'is_cbd_locked') def _check_cbd_lock_partner(self): - cbd_term = self.env['account.payment.term'].browse(26) + # cbd_term = self.env['account.payment.term'].browse(26) for rec in self: - if rec.is_cbd_locked and rec.property_payment_term_id and rec.property_payment_term_id != cbd_term: + if rec.is_cbd_locked: raise ValidationError( "Partner ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." ) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8a595d8e..89bf0d25 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -404,22 +404,22 @@ class SaleOrder(models.Model): for order in self: order.partner_is_cbd_locked = order.partner_id.is_cbd_locked - @api.onchange('payment_term_id') - def _onchange_partner_payment_term(self): - cbd_term = self.env['account.payment.term'].browse(26) - for rec in self: - if rec.partner_id and rec.partner_id.is_cbd_locked and cbd_term: - rec.payment_term_id = cbd_term + # @api.onchange('payment_term_id') + # def _onchange_partner_payment_term(self): + # cbd_term = self.env['account.payment.term'].browse(26) + # for rec in self: + # if rec.partner_id and rec.partner_id.is_cbd_locked and cbd_term: + # rec.payment_term_id = cbd_term @api.constrains('payment_term_id', 'partner_id', 'state') def _check_cbd_lock_sale_order(self): - cbd_term = self.env['account.payment.term'].browse(26) + # cbd_term = self.env['account.payment.term'].browse(26) for rec in self: if rec.state == 'draft' and rec.partner_id.is_cbd_locked: - if rec.payment_term_id and rec.payment_term_id != cbd_term: - raise ValidationError( - "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." - ) + # if rec.payment_term_id and rec.payment_term_id != cbd_term: + raise ValidationError( + "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." + ) @api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual') def _compute_payment_state_custom(self): -- cgit v1.2.3 From 4ce4b37fa551e1059cdb5f64f98c53d4289d3268 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 16:04:39 +0700 Subject: dunning done --- indoteknik_custom/models/approval_payment_term.py | 3 ++- indoteknik_custom/models/dunning_run.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/approval_payment_term.py b/indoteknik_custom/models/approval_payment_term.py index 85fd1613..61339b99 100644 --- a/indoteknik_custom/models/approval_payment_term.py +++ b/indoteknik_custom/models/approval_payment_term.py @@ -106,7 +106,8 @@ class ApprovalPaymentTerm(models.Model): if changes: timestamp = fields.Datetime.now().strftime('%Y-%m-%d %H:%M:%S') - rec.change_log_688 = f"{timestamp} - Perubahan oleh Widya:\n" + "\n".join(changes) + user = self.env.user + rec.change_log_688 = f"{timestamp} - Perubahan oleh {user.name}:\n" + "\n".join(changes) @staticmethod diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index eb6f06dc..40e5f71e 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -1,6 +1,6 @@ from odoo import models, api, fields from odoo.exceptions import AccessError, UserError, ValidationError -from datetime import timedelta +from datetime import timedelta, date import logging @@ -32,7 +32,17 @@ class DunningRun(models.Model): comment = fields.Char(string='Comment') grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") is_paid = fields.Boolean(string='Sudah Bayar?') - + tanggal_pembayaran = fields.Date( + string='Tanggal Pembayaran', + compute="_compute_tanggal_pembayaran", + readonly=True, + ) + + @api.depends('is_paid') + def _compute_tanggal_pembayaran(self): + today = fields.Date.context_today(self) + for rec in self: + rec.tanggal_pembayaran = today if rec.is_paid else False def _compute_grand_total(self): for record in self: grand_total = 0 -- cgit v1.2.3 From 7887ebd226edebbb3261c802c38f0d0925a17f91 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 16:04:47 +0700 Subject: dunning done --- indoteknik_custom/views/dunning_run.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index 7623666b..7c5f4af6 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -76,6 +76,7 @@ + -- cgit v1.2.3 From 8aceb84b2cf029308903b39bfe139a05ea8f73f6 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 16:08:34 +0700 Subject: dunning done --- indoteknik_custom/models/dunning_run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 40e5f71e..557eacef 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -43,6 +43,7 @@ class DunningRun(models.Model): today = fields.Date.context_today(self) for rec in self: rec.tanggal_pembayaran = today if rec.is_paid else False + def _compute_grand_total(self): for record in self: grand_total = 0 -- cgit v1.2.3 From e276a3bbf0a2726cbfa6bb5606d2dfbfe1e03143 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 10 Sep 2025 18:34:49 +0700 Subject: Remove tanggal bayar and is paid --- indoteknik_custom/models/dunning_run.py | 12 ------------ indoteknik_custom/views/dunning_run.xml | 3 +-- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 557eacef..9feea1d1 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -31,18 +31,6 @@ class DunningRun(models.Model): description = fields.Char(string='Description') comment = fields.Char(string='Comment') grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") - is_paid = fields.Boolean(string='Sudah Bayar?') - tanggal_pembayaran = fields.Date( - string='Tanggal Pembayaran', - compute="_compute_tanggal_pembayaran", - readonly=True, - ) - - @api.depends('is_paid') - def _compute_tanggal_pembayaran(self): - today = fields.Date.context_today(self) - for rec in self: - rec.tanggal_pembayaran = today if rec.is_paid else False def _compute_grand_total(self): for record in self: diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index 7c5f4af6..fbd9dc72 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -75,8 +75,7 @@ - - + -- cgit v1.2.3 From 6a56c1d6bfa09bf5f4d190e3f8fee5265aa01c3f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 11 Sep 2025 08:12:13 +0700 Subject: remove is paid and tanggal bayar --- indoteknik_custom/models/dunning_run.py | 6 ------ indoteknik_custom/views/dunning_run.xml | 2 -- 2 files changed, 8 deletions(-) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 557eacef..5dba3f7d 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -31,12 +31,6 @@ class DunningRun(models.Model): description = fields.Char(string='Description') comment = fields.Char(string='Comment') grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") - is_paid = fields.Boolean(string='Sudah Bayar?') - tanggal_pembayaran = fields.Date( - string='Tanggal Pembayaran', - compute="_compute_tanggal_pembayaran", - readonly=True, - ) @api.depends('is_paid') def _compute_tanggal_pembayaran(self): diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index 7c5f4af6..1cac744a 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -75,8 +75,6 @@ - - -- cgit v1.2.3 From 142f1a37b0d73ea847345428acc4224ff6e49419 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 11 Sep 2025 08:57:04 +0700 Subject: purchase report --- indoteknik_custom/__manifest__.py | 2 + indoteknik_custom/report/purchase_report.xml | 162 +++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 indoteknik_custom/report/purchase_report.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 31685005..09a3aa6f 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -166,6 +166,7 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'report/purchase_report.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', 'views/public_holiday.xml', @@ -177,6 +178,7 @@ 'views/tukar_guling_po.xml', # 'views/refund_sale_order.xml', 'views/update_date_planned_po_wizard_view.xml', + # 'views/reimburse.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml new file mode 100644 index 00000000..9d7f4028 --- /dev/null +++ b/indoteknik_custom/report/purchase_report.xml @@ -0,0 +1,162 @@ + + + + + + Purchase Order (Website) + purchase.order + qweb-pdf + indoteknik_custom.report_purchaseorder_website + indoteknik_custom.report_purchaseorder_website + + ('PO - %s - %s' % (object.partner_id.name, object.name)) + + + report + + + + + + + + + + -- cgit v1.2.3 From 6c9c60324674ce1a0b46a863a6ae0ca0e8042d2f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 11 Sep 2025 09:17:17 +0700 Subject: remove tanggal bayar --- indoteknik_custom/models/dunning_run.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 5dba3f7d..9feea1d1 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -32,12 +32,6 @@ class DunningRun(models.Model): comment = fields.Char(string='Comment') grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") - @api.depends('is_paid') - def _compute_tanggal_pembayaran(self): - today = fields.Date.context_today(self) - for rec in self: - rec.tanggal_pembayaran = today if rec.is_paid else False - def _compute_grand_total(self): for record in self: grand_total = 0 -- cgit v1.2.3 From 777f284f1811a3127d98cf5bb49934dc04e75002 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 11 Sep 2025 10:32:54 +0700 Subject: hide efaktur --- indoteknik_custom/views/dunning_run.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index 1cac744a..9c28591c 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -28,7 +28,7 @@ - + -- cgit v1.2.3 From d27561901fa2f0ab7534f255351e82459c6a531e Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Thu, 11 Sep 2025 10:45:58 +0700 Subject: (andri) fix --- indoteknik_custom/models/res_partner.py | 8 -------- indoteknik_custom/views/res_partner.xml | 2 +- indoteknik_custom/views/sale_order.xml | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 0c932dbe..36570e8f 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -194,14 +194,6 @@ class ResPartner(models.Model): default=_default_payment_term, tracking=3 ) - @api.constrains('property_payment_term_id', 'is_cbd_locked') - def _check_cbd_lock_partner(self): - # cbd_term = self.env['account.payment.term'].browse(26) - for rec in self: - if rec.is_cbd_locked: - raise ValidationError( - "Partner ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." - ) @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id", "kecamatan_id") diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 7541df35..7d5be3b7 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -108,7 +108,7 @@ - 0 + 1 1 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 236a8b14..5f1a6117 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -47,7 +47,7 @@ -- cgit v1.2.3 From c5e14063c50ae06b829eca0415b9b14b03ccb0b6 Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:07:29 +0700 Subject: (andri) fix --- indoteknik_custom/models/sale_order.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d0fd7f48..dbe6d789 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -403,12 +403,6 @@ class SaleOrder(models.Model): for order in self: order.partner_is_cbd_locked = order.partner_id.is_cbd_locked - # @api.onchange('payment_term_id') - # def _onchange_partner_payment_term(self): - # cbd_term = self.env['account.payment.term'].browse(26) - # for rec in self: - # if rec.partner_id and rec.partner_id.is_cbd_locked and cbd_term: - # rec.payment_term_id = cbd_term @api.constrains('payment_term_id', 'partner_id', 'state') def _check_cbd_lock_sale_order(self): -- cgit v1.2.3 From bfecdcb24060fca79576dc4d73064ab3e98fe3e3 Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:52:59 +0700 Subject: (andri) try web external template report --- indoteknik_custom/report/report_surat_piutang.xml | 162 ++++++++++------------ 1 file changed, 74 insertions(+), 88 deletions(-) diff --git a/indoteknik_custom/report/report_surat_piutang.xml b/indoteknik_custom/report/report_surat_piutang.xml index 5cad81e6..122e9d64 100644 --- a/indoteknik_custom/report/report_surat_piutang.xml +++ b/indoteknik_custom/report/report_surat_piutang.xml @@ -1,55 +1,42 @@ + Surat Peringatan Piutang surat.piutang qweb-pdf - indoteknik_custom.report_surat_piutang_formal_custom - indoteknik_custom.report_surat_piutang_formal_custom + indoteknik_custom.report_surat_piutang + indoteknik_custom.report_surat_piutang report - - - - - + \ No newline at end of file -- cgit v1.2.3 From b3ec9281f0afe9da2b5c6bd207289de8cc5888c4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 15 Sep 2025 11:30:29 +0700 Subject: push --- indoteknik_custom/report/purchase_report.xml | 262 +++++++-------------------- 1 file changed, 67 insertions(+), 195 deletions(-) diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index a6db2923..dedd100f 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -26,237 +26,101 @@ - \ No newline at end of file + + + -- cgit v1.2.3 From 062f8e6c5bf9ad87c9de7137dd399fa42d8d66a8 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 15 Sep 2025 11:34:08 +0700 Subject: push --- indoteknik_custom/report/purchase_report.xml | 57 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index dedd100f..17d314ff 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -76,53 +76,52 @@ - - - - - - - + + + + + + + - - + - - - - + - - - + + + + + + + + + + -- cgit v1.2.3 From cf64a8c5913308c3121a55b1b4cd1acf17c86d73 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 15 Sep 2025 11:37:32 +0700 Subject: push --- indoteknik_custom/report/purchase_report.xml | 37 +++++++++------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 17d314ff..168428a6 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -76,16 +76,15 @@
DescriptionQtyUnitTaxSubtotal
DescriptionQuantityUnit PriceTaxesSubtotal
+
+ + + +
-
-
+ Description +
+
+
- + - - - - - + + + + + - - + @@ -105,29 +104,17 @@ - - - - - - - - - - - - + + +
DescriptionQuantityUnit PriceTaxesSubtotalDescriptionQuantityUnit PriceTaxesSubtotal
- Description -
-
-
+
+
- -- cgit v1.2.3 From 2b61b810f5b12f32bf837ab34c12d832d0be12eb Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:48:33 +0700 Subject: (andri) edit status dan rev validasi --- indoteknik_custom/models/letter_receivable.py | 13 ++++++++----- indoteknik_custom/views/letter_receivable.xml | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index 17963232..fe598e50 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -31,8 +31,8 @@ class SuratPiutang(models.Model): line_ids = fields.One2many("surat.piutang.line", "surat_id", string="Invoice Lines") state = fields.Selection([ ("draft", "Draft"), - ("approval_pimpinan", "Menunggu Approval Pimpinan"), - ("sent", "Sent") + ("waiting_approval", "Menunggu Approval"), + ("sent", "Approved & Sent") ], default="draft", tracking=True) send_date = fields.Datetime(string="Tanggal Kirim", tracking=True) seven_days_after_sent_date = fields.Char(string="7 Hari Setelah Tanggal Kirim", tracking=True) @@ -128,8 +128,11 @@ class SuratPiutang(models.Model): # if self.env.user.id not in pimpinan_user_ids: # raise UserError("Hanya Pimpinan yang berhak menyetujui tahap ini.") for rec in self: - if rec.state == "approval_pimpinan": - # rec.state = "sent" + if rec.state == "waiting_approval": + if rec.perihal in ("sp1", "sp2", "sp3"): + if self.env.user.id not in pimpinan_user_ids: + raise UserError("Hanya Pimpinan yang berhak menyetujui surat peringatan piutang (SP1, SP2, SP3).") + rec.state = "sent" now_utc = now_wib.astimezone(pytz.UTC).replace(tzinfo=None) rec.send_date = now_utc rec.action_send_letter() @@ -340,7 +343,7 @@ class SuratPiutang(models.Model): tahun = today.strftime("%y") vals["name"] = f"{seq}/LO/FAT/IDG/{bulan_romawi}/{tahun}" - vals["state"] = "approval_pimpinan" + vals["state"] = "waiting_approval" return super().create(vals) class SuratPiutangLine(models.Model): diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 44caadcb..334e83c4 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -10,7 +10,7 @@ @@ -25,7 +25,7 @@
- +
@@ -38,8 +38,8 @@
- - - - +
Term Of Payment: Order Date: Responsible:
+ + + +
Term Of Payment: Order Date: Responsible:
- +
- -
- Alamat Pengiriman:
+
+ Alamat Pengiriman
PT Indoteknik (Bandengan 1 Depan)
Jl. Bandengan Utara Komp A 8 B
RT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET)
JK 14440 - Indonesia
- Nama Vendor:
+
+ Nama Vendor


- @@ -75,38 +75,37 @@
- +
- - - - - - + + + + + + - - - - - + - - - - - - + @@ -116,34 +115,36 @@
DescriptionQuantityUnit PriceTaxesSubtotal
No. & DescriptionQuantityUnit PriceTaxesSubtotal
+ +
+ . + + + +
+
- - - - +
Subtotal
+ + + - - + + - - - + + +
Subtotal
TaxesTaxes
Total
Total
-
+

- + + + -- cgit v1.2.3 From a7baa3c7f42d33064039cdea7ce6d3ae6d515f81 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 15 Sep 2025 14:30:12 +0700 Subject: push --- indoteknik_custom/report/purchase_report.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 333b807a..f3d203fc 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -105,7 +105,7 @@ - +
-- cgit v1.2.3 From 570087805ec46b8af7651187cfcf0ecef2733912 Mon Sep 17 00:00:00 2001 From: AndriFP <113114423+andrifp@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:45:43 +0700 Subject: (andri) rev penamaan surat dan doc --- indoteknik_custom/models/letter_receivable.py | 5 +++-- indoteknik_custom/report/report_surat_piutang.xml | 1 + indoteknik_custom/views/letter_receivable.xml | 4 ++-- indoteknik_custom/views/letter_receivable_mail_template.xml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index fe598e50..18485f01 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -215,7 +215,7 @@ class SuratPiutang(models.Model): attachment_base64 = base64.b64encode(pdf_content) attachment = self.env['ir.attachment'].create({ - 'name': f"Surat Piutang {self.name}.pdf", + 'name': f"{self.perihal_label} - {self.partner_id.name}.pdf", 'type': 'binary', 'datas': attachment_base64, 'res_model': 'surat.piutang', @@ -224,7 +224,8 @@ class SuratPiutang(models.Model): }) values = { - 'subject': template.subject.replace('${object.name}', self.name or ''), + # 'subject': template.subject.replace('${object.name}', self.name or ''), + 'subject': perihal_map.get(self.perihal, self.perihal or '') + " - " + (self.partner_id.name or ''), 'email_to': self.tujuan_email, 'email_from': 'finance@indoteknik.co.id', 'body_html': body_html, diff --git a/indoteknik_custom/report/report_surat_piutang.xml b/indoteknik_custom/report/report_surat_piutang.xml index 4685f5a6..770aa535 100644 --- a/indoteknik_custom/report/report_surat_piutang.xml +++ b/indoteknik_custom/report/report_surat_piutang.xml @@ -9,6 +9,7 @@ qweb-pdf indoteknik_custom.report_surat_piutang indoteknik_custom.report_surat_piutang + '%s - %s' % (object.perihal_label or '', object.partner_id.name or '') report diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 334e83c4..ad1f6281 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -26,8 +26,8 @@
-