diff --git a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js index b305a8c..6c1552d 100644 --- a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js +++ b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js @@ -31,6 +31,7 @@ function fill_from_reports(frm) { set_table(frm, "əlavə_1", r.message.elave_1); set_table(frm, "əlavə_2", r.message.elave_2); set_table(frm, "əlavə_2_2", r.message.elave_2_2); + set_table(frm, "müəssisə_malın_dəyəri", r.message.elave_8_2); frm.refresh_fields(); frappe.show_alert({ message: __("Əlavələr hesabatlardan dolduruldu"), indicator: "green" }); diff --git a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py index 7ba8afd..457d73a 100644 --- a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py +++ b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.utils import flt, getdate, add_months, cint from datetime import datetime, date import calendar +import re class Declarationofvalueaddedtax(Document): pass @@ -26,6 +27,21 @@ ACT_TYPE_MAP = { } +def _split_cert_no(cert_no): + """Split a combined certificate number into (series, number). + + Leading letters are the series, the trailing digits are the number, e.g. + "Az10000001" -> ("Az", "10000001"). If there are no leading letters the + whole value becomes the number. The series field is 3 chars wide, so it is + capped to 3 to avoid truncation errors. + """ + s = (cert_no or "").strip() + m = re.match(r"^([^\d]+)(\d.*)$", s) + if m: + return m.group(1).strip()[:3], m.group(2).strip() + return "", s + + def _period_filters(year, month): """Build report filters (company + month date range) from year + month.""" from frappe.utils import get_last_day @@ -58,6 +74,9 @@ def populate_vat_tables(year, month): from taxes_az.taxes_az.report.agricultural_goods_stock_report.agricultural_goods_stock_report import ( get_data as agri_get_data, ) + from taxes_az.taxes_az.report.certificate_vat_report.certificate_vat_report import ( + get_data as certificate_get_data, + ) if not month or not year: return {"success": False, "error": "Year and month are required"} @@ -98,10 +117,25 @@ def populate_vat_tables(year, month): "hesabatdövründəsatılmışmallaragörəticarətəlavəsininədvməbləği": r.get("trade_markup_vat"), }) + # Əlavə 8 Tablo 2 (müəssisə_malın_dəyəri) — one row per certificate presented + # on a sales invoice, with the value of goods supplied under it. The full + # item amount is kept per certificate (no splitting across certificates). + elave_8_2 = [] + for r in (certificate_get_data(filters) or []): + series, number = _split_cert_no(r.get("cert_no")) + elave_8_2.append({ + "şəxsinadı": r.get("taxpayer_full_name"), + "şəxsinvöeni": r.get("voen") or None, + "sertifikatınseriyası": series, + "nsi": number, + "xidmətindəyəri": r.get("value"), + }) + return { "success": True, "elave_1": elave_1, "elave_2": elave_2, "elave_2_2": elave_2_2, + "elave_8_2": elave_8_2, "period": filters, } \ No newline at end of file diff --git a/taxes_az/taxes_az/report/certificate_vat_report/__init__.py b/taxes_az/taxes_az/report/certificate_vat_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.js b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.js new file mode 100644 index 0000000..db4b6d7 --- /dev/null +++ b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.js @@ -0,0 +1,29 @@ +// Copyright (c) 2026, Your Company and contributors +// For license information, please see license.txt + +frappe.query_reports["Certificate VAT Report"] = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.month_start(), + reqd: 1, + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.month_end(), + reqd: 1, + }, + ], +}; diff --git a/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.json b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.json new file mode 100644 index 0000000..9c73808 --- /dev/null +++ b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 1, + "columns": [], + "creation": "2026-06-29 13:00:00.000000", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2026-06-29 13:00:00.000000", + "modified_by": "Administrator", + "module": "Taxes Az", + "name": "Certificate VAT Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Invoice", + "report_name": "Certificate VAT Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "System Manager" + } + ], + "timeout": 0 +} diff --git a/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.py b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.py new file mode 100644 index 0000000..e78058b --- /dev/null +++ b/taxes_az/taxes_az/report/certificate_vat_report/certificate_vat_report.py @@ -0,0 +1,97 @@ +# Copyright (c) 2026, Your Company and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ + +# Cədvəl 23 — "ƏDV - Sertifikat". +# +# One row per (item certificate). Certificates are stored on the Sales Invoice +# parent in the `custom_item_certificates` table (doctype "Sales Invoice +# Certificate"), each row linked to its item row by `parent_row` == +# `Sales Invoice Item.custom_si_row_key`. If one item carries 3 certificates we +# emit 3 rows, each with the item's FULL amount — the value is intentionally not +# split across certificates. + + +def execute(filters=None): + filters = filters or {} + return get_columns(), get_data(filters) + + +def get_columns(): + return [ + { + "fieldname": "sales_invoice", + "label": _("Sales Invoice"), + "fieldtype": "Link", + "options": "Sales Invoice", + "width": 160, + }, + { + "fieldname": "posting_date", + "label": _("Date"), + "fieldtype": "Date", + "width": 100, + }, + { + "fieldname": "cert_no", + "label": _("Sertifikatın seriya / nömrəsi"), + "fieldtype": "Data", + "width": 200, + }, + { + "fieldname": "voen", + "label": _("Sertifikat təqdim olunan şəxsin Vöeni"), + "fieldtype": "Data", + "width": 170, + }, + { + "fieldname": "taxpayer_full_name", + "label": _("Sertifikat təqdim olunan şəxsin adı"), + "fieldtype": "Data", + "width": 280, + }, + { + "fieldname": "value", + "label": _("Sertifikat altında təqdim olunan malların (işlərin, xidmətlərin) dəyəri"), + "fieldtype": "Currency", + "width": 230, + }, + ] + + +def get_data(filters): + conditions = "" + if filters.get("company"): + conditions += " AND si.company = %(company)s" + if filters.get("from_date"): + conditions += " AND si.posting_date >= %(from_date)s" + if filters.get("to_date"): + conditions += " AND si.posting_date <= %(to_date)s" + + return frappe.db.sql( + """ + SELECT + sic.parent AS sales_invoice, + si.posting_date AS posting_date, + epc.cert_no AS cert_no, + epc.voen AS voen, + epc.taxpayer_full_name AS taxpayer_full_name, + sii.amount AS value + FROM `tabSales Invoice Certificate` sic + INNER JOIN `tabSales Invoice` si + ON si.name = sic.parent AND si.docstatus = 1 + INNER JOIN `tabSales Invoice Item` sii + ON sii.parent = sic.parent + AND sii.custom_si_row_key = sic.parent_row + LEFT JOIN `tabE-Taxes Presented Certificate` epc + ON epc.name = sic.certificate + WHERE sic.parenttype = 'Sales Invoice' + AND sic.certificate IS NOT NULL AND sic.certificate != '' + {conditions} + ORDER BY si.posting_date DESC, sic.parent, epc.cert_no + """.format(conditions=conditions), + filters, + as_dict=True, + )