feat(vat): Certificate VAT Report (Cədvəl 23) + auto-fill Əlavə 8
- New Script Report "Certificate VAT Report": one row per sales-invoice item certificate (cert no, VÖEN, taxpayer, value), full item amount per certificate with no splitting; filters company + date range, submitted invoices only. - Auto-fill VAT declaration Əlavə 8 Table 2 (müəssisə_malın_dəyəri) from that report inside populate_vat_tables; split cert_no into series (letters, max 3) and number (digits). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
71d9e303ce
commit
7cfae04159
|
|
@ -31,6 +31,7 @@ function fill_from_reports(frm) {
|
||||||
set_table(frm, "əlavə_1", r.message.elave_1);
|
set_table(frm, "əlavə_1", r.message.elave_1);
|
||||||
set_table(frm, "əlavə_2", r.message.elave_2);
|
set_table(frm, "əlavə_2", r.message.elave_2);
|
||||||
set_table(frm, "əlavə_2_2", r.message.elave_2_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();
|
frm.refresh_fields();
|
||||||
frappe.show_alert({ message: __("Əlavələr hesabatlardan dolduruldu"), indicator: "green" });
|
frappe.show_alert({ message: __("Əlavələr hesabatlardan dolduruldu"), indicator: "green" });
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from frappe import _
|
||||||
from frappe.utils import flt, getdate, add_months, cint
|
from frappe.utils import flt, getdate, add_months, cint
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
import calendar
|
import calendar
|
||||||
|
import re
|
||||||
|
|
||||||
class Declarationofvalueaddedtax(Document):
|
class Declarationofvalueaddedtax(Document):
|
||||||
pass
|
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):
|
def _period_filters(year, month):
|
||||||
"""Build report filters (company + month date range) from year + month."""
|
"""Build report filters (company + month date range) from year + month."""
|
||||||
from frappe.utils import get_last_day
|
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 (
|
from taxes_az.taxes_az.report.agricultural_goods_stock_report.agricultural_goods_stock_report import (
|
||||||
get_data as agri_get_data,
|
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:
|
if not month or not year:
|
||||||
return {"success": False, "error": "Year and month are required"}
|
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"),
|
"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 {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"elave_1": elave_1,
|
"elave_1": elave_1,
|
||||||
"elave_2": elave_2,
|
"elave_2": elave_2,
|
||||||
"elave_2_2": elave_2_2,
|
"elave_2_2": elave_2_2,
|
||||||
|
"elave_8_2": elave_8_2,
|
||||||
"period": filters,
|
"period": filters,
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue