summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-09-17 18:05:45 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-09-17 18:05:45 +0700
commitade0fe34d2d77fa9ee8e275cb60301b325710bd1 (patch)
treeeeeb88d8c7593a410244cfea20c379e39f2713aa
parent776d2eefc8d43462eee3992992088e066fecfe21 (diff)
coretax
-rwxr-xr-xfixco_custom/models/__init__.py3
-rw-r--r--fixco_custom/models/account_move.py16
-rw-r--r--fixco_custom/models/coretax_faktur.py176
-rwxr-xr-xfixco_custom/models/partner.py5
-rwxr-xr-xfixco_custom/security/ir.model.access.csv1
-rw-r--r--fixco_custom/views/account_move.xml8
6 files changed, 207 insertions, 2 deletions
diff --git a/fixco_custom/models/__init__.py b/fixco_custom/models/__init__.py
index 23f28a9..27ca4e1 100755
--- a/fixco_custom/models/__init__.py
+++ b/fixco_custom/models/__init__.py
@@ -29,4 +29,5 @@ from . import product_public_category
from . import reordering_rule
from . import update_depreciation_move_wizard
from . import invoice_reklas
-from . import uangmuka_pembelian \ No newline at end of file
+from . import uangmuka_pembelian
+from . import coretax_faktur \ No newline at end of file
diff --git a/fixco_custom/models/account_move.py b/fixco_custom/models/account_move.py
index 836b878..bb84573 100644
--- a/fixco_custom/models/account_move.py
+++ b/fixco_custom/models/account_move.py
@@ -42,6 +42,22 @@ class AccountMove(models.Model):
reklas_used = fields.Boolean('Reklas Used?', compute='_compute_reklas_used')
reklas_used_by = fields.Many2one('account.move', string='Reklas Used By', compute='_compute_reklas_used')
+ def export_faktur_to_xml(self):
+ valid_invoices = self
+
+ coretax_faktur = self.env['coretax.faktur'].create({})
+
+ response = coretax_faktur.export_to_download(
+ invoices=valid_invoices
+ )
+
+ valid_invoices.write({
+ 'is_efaktur_exported': True,
+ 'date_efaktur_exported': datetime.utcnow(),
+ })
+
+ return response
+
@api.depends('line_ids.reconciled', 'line_ids.matching_number')
def _compute_reklas_used(self):
for move in self:
diff --git a/fixco_custom/models/coretax_faktur.py b/fixco_custom/models/coretax_faktur.py
new file mode 100644
index 0000000..7790904
--- /dev/null
+++ b/fixco_custom/models/coretax_faktur.py
@@ -0,0 +1,176 @@
+from odoo import models, fields
+import xml.etree.ElementTree as ET
+from xml.dom import minidom
+import base64
+import re
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class CoretaxFaktur(models.Model):
+ _name = 'coretax.faktur'
+ _description = 'Export Faktur ke XML'
+
+ export_file = fields.Binary(string="Export File", )
+ export_filename = fields.Char(string="Export File", )
+
+ DISCOUNT_ACCOUNT_ID = 426
+
+ def validate_and_format_number(self, input_number):
+ # Hapus semua karakter non-digit
+ cleaned_number = re.sub(r'\D', '', input_number)
+
+ total_sum = sum(int(char) for char in cleaned_number)
+
+ if total_sum == 0:
+ return '0000000000000000'
+
+ # Hitung jumlah digit
+ digit_count = len(cleaned_number)
+
+ # Jika jumlah digit kurang dari 15, tambahkan nol di depan
+ if digit_count < 16:
+ cleaned_number = cleaned_number.zfill(16)
+
+ return cleaned_number
+
+ def generate_xml(self, invoices=None, down_payments=False):
+ # Buat root XML
+ root = ET.Element('TaxInvoiceBulk', {
+ 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
+ 'xsi:noNamespaceSchemaLocation': "TaxInvoice.xsd"
+ })
+ ET.SubElement(root, 'TIN').text = '0742260227086000'
+
+ # Tambahkan elemen ListOfTaxInvoice
+ list_of_tax_invoice = ET.SubElement(root, 'ListOfTaxInvoice')
+
+ for invoice in invoices:
+ tax_invoice = ET.SubElement(list_of_tax_invoice, 'TaxInvoice')
+ buyerTIN = self.validate_and_format_number(invoice.partner_id.npwp)
+ nitku = invoice.partner_id.nitku
+ formula = nitku if nitku else buyerTIN.ljust(len(buyerTIN) + 6, '0')
+ buyerIDTKU = formula if sum(int(char) for char in buyerTIN) > 0 else '000000'
+
+ # Tambahkan elemen faktur
+ ET.SubElement(tax_invoice, 'TaxInvoiceDate').text = invoice.invoice_date.strftime(
+ '%Y-%m-%d') if invoice.invoice_date else ''
+ ET.SubElement(tax_invoice, 'TaxInvoiceOpt').text = 'Normal'
+ ET.SubElement(tax_invoice, 'TrxCode').text = '04'
+ ET.SubElement(tax_invoice, 'AddInfo')
+ ET.SubElement(tax_invoice, 'CustomDoc')
+ ET.SubElement(tax_invoice, 'CustomDocMonthYear')
+ ET.SubElement(tax_invoice, 'RefDesc').text = invoice.name
+ ET.SubElement(tax_invoice, 'FacilityStamp')
+ ET.SubElement(tax_invoice, 'SellerIDTKU').text = '0742260227086000000000'
+ ET.SubElement(tax_invoice, 'BuyerTin').text = buyerTIN
+ ET.SubElement(tax_invoice, 'BuyerDocument').text = 'TIN' if sum(
+ int(char) for char in buyerTIN) > 0 else 'Other ID'
+ ET.SubElement(tax_invoice, 'BuyerCountry').text = 'IDN'
+ ET.SubElement(tax_invoice, 'BuyerDocumentNumber').text = '-' if sum(
+ int(char) for char in buyerTIN) > 0 else str(invoice.partner_id.id)
+ ET.SubElement(tax_invoice, 'BuyerName').text = invoice.partner_id.nama_wajib_pajak or ''
+ ET.SubElement(tax_invoice, 'BuyerAdress').text = invoice.partner_id.alamat_lengkap_text or ''
+ ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or ''
+ ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU
+
+ product_lines = invoice.invoice_line_ids.filtered(
+ lambda l: not l.display_type and hasattr(l, 'account_id') and
+ l.account_id and l.product_id and
+ l.account_id.id != self.DISCOUNT_ACCOUNT_ID and
+ l.quantity != -1
+ )
+
+ discount_lines = invoice.invoice_line_ids.filtered(
+ lambda l: not l.display_type and (
+ (hasattr(l, 'account_id') and l.account_id and
+ l.account_id.id == self.DISCOUNT_ACCOUNT_ID) or
+ (l.quantity == -1)
+ )
+ )
+
+ # Calculate totals
+ total_product_amount = sum(line.get('price_subtotal', 0) if isinstance(line, dict)
+ else line.price_subtotal for line in product_lines)
+ if total_product_amount == 0:
+ total_product_amount = 1 # Avoid division by zero
+
+ total_discount_amount = abs(sum(line.price_subtotal for line in discount_lines))
+
+ # Tambahkan elemen ListOfGoodService
+ list_of_good_service = ET.SubElement(tax_invoice, 'ListOfGoodService')
+
+ for line in product_lines:
+ # Handle both dict (converted sale lines) and normal invoice lines
+ if isinstance(line, dict):
+ line_price_subtotal = line['price_subtotal']
+ line_quantity = line['quantity']
+ line_name = line['name']
+ line_price_unit = line['price_unit']
+ else:
+ line_price_subtotal = line.price_subtotal
+ line_quantity = line.quantity
+ line_name = line.name
+ line_price_unit = line.price_unit
+
+ # Calculate prorated discount
+ line_proportion = line_price_subtotal / total_product_amount
+ line_discount = total_discount_amount * line_proportion
+
+ subtotal = line_price_subtotal
+ quantity = line_quantity
+ total_discount = round(line_discount, 2)
+
+ # Calculate other tax values
+ otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0
+ vat_amount = round(otherTaxBase * 0.12, 2)
+
+ price_per_unit = round(subtotal / quantity, 2) if quantity else 0
+
+ # Create the line in XML
+ good_service = ET.SubElement(list_of_good_service, 'GoodService')
+ ET.SubElement(good_service, 'Opt').text = 'A'
+ ET.SubElement(good_service, 'Code').text = '000000'
+ ET.SubElement(good_service, 'Name').text = line_name
+ ET.SubElement(good_service, 'Unit').text = 'UM.0018'
+ # ET.SubElement(good_service, 'Price').text = str(round(line_price_unit, 2)) if line_price_unit else '0'
+ ET.SubElement(good_service, 'Price').text = str(price_per_unit)
+ ET.SubElement(good_service, 'Qty').text = str(quantity)
+ ET.SubElement(good_service, 'TotalDiscount').text = str(total_discount)
+ ET.SubElement(good_service, 'TaxBase').text = str(round(subtotal)) if subtotal else '0'
+ ET.SubElement(good_service, 'OtherTaxBase').text = str(otherTaxBase)
+ ET.SubElement(good_service, 'VATRate').text = '12'
+ ET.SubElement(good_service, 'VAT').text = str(vat_amount)
+ ET.SubElement(good_service, 'STLGRate').text = '0'
+ ET.SubElement(good_service, 'STLG').text = '0'
+
+ # Pretty print XML
+ xml_str = ET.tostring(root, encoding='utf-8')
+ xml_pretty = minidom.parseString(xml_str).toprettyxml(indent=" ")
+ return xml_pretty
+
+ def export_to_download(self, invoices):
+ # Generate XML content
+ xml_content = self.generate_xml(invoices)
+
+ # Encode content to Base64
+ xml_encoded = base64.b64encode(xml_content.encode('utf-8'))
+
+ # Buat attachment untuk XML
+ attachment = self.env['ir.attachment'].create({
+ 'name': 'Faktur_XML.xml',
+ 'type': 'binary',
+ 'datas': xml_encoded,
+ 'mimetype': 'application/xml',
+ 'store_fname': 'faktur_xml.xml',
+ })
+
+ # Kembalikan URL untuk download dengan header 'Content-Disposition'
+ response = {
+ 'type': 'ir.actions.act_url',
+ 'url': '/web/content/{}?download=true'.format(attachment.id),
+ 'target': 'self',
+ }
+
+ return response
diff --git a/fixco_custom/models/partner.py b/fixco_custom/models/partner.py
index bd17ded..c8e95b0 100755
--- a/fixco_custom/models/partner.py
+++ b/fixco_custom/models/partner.py
@@ -15,4 +15,7 @@ class Partner(models.Model):
[('online', 'Online'),
('offline', 'Offline')],
string='Customer Type'
- ) \ No newline at end of file
+ )
+ sppkp = fields.Char(string="SPPKP", tracking=True)
+ nitku = fields.Char(string="NITKU", tracking=True)
+ npwp = fields.Char(string="NPWP", tracking=True) \ No newline at end of file
diff --git a/fixco_custom/security/ir.model.access.csv b/fixco_custom/security/ir.model.access.csv
index d1cd618..8103ca2 100755
--- a/fixco_custom/security/ir.model.access.csv
+++ b/fixco_custom/security/ir.model.access.csv
@@ -36,4 +36,5 @@ access_purchasing_job_note_wizard,access.purchasing.job.note.wizard,model_purcha
access_update_depreciation_move_wizard,access.update.depreciation.move.wizard,model_update_depreciation_move_wizard,,1,1,1,1
access_invoice_reklas,access.invoice.reklas,model_invoice_reklas,,1,1,1,1
access_uangmuka_pembelian,access.uangmuka.pembelian,model_uangmuka_pembelian,,1,1,1,1
+access_coretax_faktur,access.coretax.faktur,model_coretax_faktur,,1,1,1,1
access_report.fixco_custom.report_picking_list_custom,access.report.fixco_custom.report_picking_list_custom,model_report_fixco_custom_report_picking_list_custom,,1,1,1,1
diff --git a/fixco_custom/views/account_move.xml b/fixco_custom/views/account_move.xml
index 88d3b46..3b122dd 100644
--- a/fixco_custom/views/account_move.xml
+++ b/fixco_custom/views/account_move.xml
@@ -115,5 +115,13 @@
</field>
</field>
</record>
+
+ <record id="action_export_faktur" model="ir.actions.server">
+ <field name="name">Export Faktur ke XML</field>
+ <field name="model_id" ref="account.model_account_move" />
+ <field name="binding_model_id" ref="account.model_account_move" />
+ <field name="state">code</field>
+ <field name="code">action = records.export_faktur_to_xml()</field>
+ </record>
</data>
</odoo> \ No newline at end of file