auto-populate hissə_1 from Employee Payroll Register on year/quarter change
Redesign Single Declaration-Elave1_Hisse1 child table with all report columns (26 fields), add server-side aggregation per employee with insurance formula (işçi + şirkət) × 3, and client-side triggers on il/hesabatdövrü fields. Also fix backtick escaping for Unicode field names in frappe.db.get_value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4addeb8a05
commit
63e9ec5ee4
|
|
@ -6,60 +6,194 @@
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"soyadadataad\u0131",
|
"employee",
|
||||||
"fin",
|
"employeename",
|
||||||
"g\u0259lirl\u0259rc\u0259mi",
|
"dateofjoining",
|
||||||
"vergim\u0259bl\u0259\u011fi",
|
"branch",
|
||||||
|
"department",
|
||||||
|
"designation",
|
||||||
|
"leavewithoutpay",
|
||||||
|
"absentdays",
|
||||||
|
"paymentdays",
|
||||||
|
"\u0259sas\u0259m\u0259khaqq\u0131",
|
||||||
|
"m\u0259zuniyy\u0259tkompensasiyas\u0131",
|
||||||
|
"dsmfx\u0259rc\u015firk\u0259t",
|
||||||
|
"icbaritibbis\u0131\u011fortax\u0259rc\u015firk\u0259t2",
|
||||||
|
"i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131x\u0259rc\u015firk\u0259t05",
|
||||||
|
"grosspay",
|
||||||
|
"dsmfi\u015f\u00e7i",
|
||||||
|
"dsmf\u015firk\u0259t",
|
||||||
|
"icbaritibbis\u0131\u011fortai\u015f\u00e7i2",
|
||||||
|
"icbaritibbis\u0131\u011forta\u015firk\u0259t2",
|
||||||
|
"i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131i\u015f\u00e7i05",
|
||||||
|
"i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131\u015firk\u0259t05",
|
||||||
|
"totaldeduction",
|
||||||
|
"netpay",
|
||||||
"hesablanm\u0131\u015fmdsshaqq\u0131",
|
"hesablanm\u0131\u015fmdsshaqq\u0131",
|
||||||
"hesablanm\u0131\u015fi\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131",
|
"hesablanm\u0131\u015fi\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131",
|
||||||
"hesablanm\u0131\u015ficbaritibbis\u0131\u011fortahaqq\u0131"
|
"hesablanm\u0131\u015ficbaritibbis\u0131\u011fortahaqq\u0131"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "soyadadataad\u0131",
|
"fieldname": "employee",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Soyad, ad, ata ad\u0131",
|
"label": "Employee",
|
||||||
|
"options": "Employee",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fin",
|
"fieldname": "employeename",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "F\u0130N",
|
"label": "Employee Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "g\u0259lirl\u0259rc\u0259mi",
|
"fieldname": "dateofjoining",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Date",
|
||||||
"in_list_view": 1,
|
"label": "Date of Joining",
|
||||||
"label": "G\u0259lirl\u0259r c\u0259mi",
|
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "vergim\u0259bl\u0259\u011fi",
|
"fieldname": "branch",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"label": "Branch",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "department",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Department",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "designation",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Designation",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "leavewithoutpay",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Leave Without Pay",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "absentdays",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Absent Days",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "paymentdays",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Payment Days",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "\u0259sas\u0259m\u0259khaqq\u0131",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u018esas \u0259m\u0259k haqq\u0131",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "m\u0259zuniyy\u0259tkompensasiyas\u0131",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "M\u0259zuniyy\u0259t Kompensasiyas\u0131",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dsmfx\u0259rc\u015firk\u0259t",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "DSMF (x\u0259rc \u015firk\u0259t)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "icbaritibbis\u0131\u011fortax\u0259rc\u015firk\u0259t2",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130cbari tibbi s\u0131\u011forta (x\u0259rc \u015firk\u0259t 2%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131x\u0259rc\u015firk\u0259t05",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130\u015fsizlikd\u0259n s\u0131\u011forta haqq\u0131 (x\u0259rc \u015firk\u0259t 0,5%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "grosspay",
|
||||||
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Vergi m\u0259bl\u0259\u011fi",
|
"label": "Gross Pay",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dsmfi\u015f\u00e7i",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "DSMF (i\u015f\u00e7i)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dsmf\u015firk\u0259t",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "DSMF (\u015firk\u0259t)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "icbaritibbis\u0131\u011fortai\u015f\u00e7i2",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130cbari tibbi s\u0131\u011forta (i\u015f\u00e7i 2%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "icbaritibbis\u0131\u011forta\u015firk\u0259t2",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130cbari tibbi s\u0131\u011forta (\u015firk\u0259t 2%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131i\u015f\u00e7i05",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130\u015fsizlikd\u0259n s\u0131\u011forta haqq\u0131 (i\u015f\u00e7i 0,5%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "i\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131\u015firk\u0259t05",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "\u0130\u015fsizlikd\u0259n s\u0131\u011forta haqq\u0131 (\u015firk\u0259t 0,5%)",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "totaldeduction",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Deduction",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "netpay",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Net Pay",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "hesablanm\u0131\u015fmdsshaqq\u0131",
|
"fieldname": "hesablanm\u0131\u015fmdsshaqq\u0131",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Hesablanm\u0131\u015f MDSS haqq\u0131",
|
"label": "Hesablanm\u0131\u015f MDSS haqq\u0131",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "hesablanm\u0131\u015fi\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131",
|
"fieldname": "hesablanm\u0131\u015fi\u015fsizlikd\u0259ns\u0131\u011fortahaqq\u0131",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Hesablanm\u0131\u015f i\u015fsizlikd\u0259n s\u0131\u011forta haqq\u0131",
|
"label": "Hesablanm\u0131\u015f i\u015fsizlikd\u0259n s\u0131\u011forta haqq\u0131",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "hesablanm\u0131\u015ficbaritibbis\u0131\u011fortahaqq\u0131",
|
"fieldname": "hesablanm\u0131\u015ficbaritibbis\u0131\u011fortahaqq\u0131",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Hesablanm\u0131\u015f icbari tibbi s\u0131\u011forta haqq\u0131",
|
"label": "Hesablanm\u0131\u015f icbari tibbi s\u0131\u011forta haqq\u0131",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
|
@ -68,8 +202,8 @@
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-03-26 18:02:10.219887",
|
"modified": "2026-03-30 12:00:00.000000",
|
||||||
"modified_by": "nihad_m@jeysoft.az",
|
"modified_by": "Administrator",
|
||||||
"module": "Taxes Az",
|
"module": "Taxes Az",
|
||||||
"name": "Single Declaration-Elave1_Hisse1",
|
"name": "Single Declaration-Elave1_Hisse1",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ function fillFromCompanyDefaults(frm) {
|
||||||
frappe.db.get_value(
|
frappe.db.get_value(
|
||||||
'Company',
|
'Company',
|
||||||
company,
|
company,
|
||||||
['mdssüzrə', 'icbaritibbisığortaüzrə', 'işsizlikdənsığortaüzrə', 'main_activity']
|
['`mdssüzrə`', '`icbaritibbisığortaüzrə`', '`işsizlikdənsığortaüzrə`', '`main_activity`']
|
||||||
).then(r => {
|
).then(r => {
|
||||||
if (!r || !r.message) return;
|
if (!r || !r.message) return;
|
||||||
const v = r.message;
|
const v = r.message;
|
||||||
|
|
@ -25,23 +25,24 @@ frappe.ui.form.on('Single declaration related to salaried and non-salaried work'
|
||||||
|
|
||||||
il: function(frm) {
|
il: function(frm) {
|
||||||
fillFromCompanyDefaults(frm);
|
fillFromCompanyDefaults(frm);
|
||||||
}
|
reload_hisse_1_data(frm);
|
||||||
});
|
},
|
||||||
|
|
||||||
|
hesabatdövrü: function(frm) {
|
||||||
|
reload_hisse_1_data(frm);
|
||||||
|
},
|
||||||
|
|
||||||
// Код для экспорта XML
|
|
||||||
frappe.ui.form.on('Single declaration related to salaried and non-salaried work', {
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.add_custom_button(__('Export XML'), function() {
|
frm.add_custom_button(__('Export XML'), function() {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'taxes_az.taxes_az.doctype.xml_mapping_tool.xml_mapping_tool.generate_xml',
|
method: 'taxes_az.taxes_az.doctype.xml_mapping_tool.xml_mapping_tool.generate_xml',
|
||||||
args: {
|
args: {
|
||||||
mapping_tool: 'Single declaration related to salaried and non-salaried work', // Замените на имя вашего документа XML Mapping Tool
|
mapping_tool: 'Single declaration related to salaried and non-salaried work',
|
||||||
doctype: 'Single declaration related to salaried and non-salaried work', // Замените на имя DocType
|
doctype: 'Single declaration related to salaried and non-salaried work',
|
||||||
docname: frm.docname
|
docname: frm.docname
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
// Создаем временный элемент для скачивания
|
|
||||||
var element = document.createElement('a');
|
var element = document.createElement('a');
|
||||||
element.setAttribute('href', 'data:text/xml;charset=utf-8,' + encodeURIComponent(r.message));
|
element.setAttribute('href', 'data:text/xml;charset=utf-8,' + encodeURIComponent(r.message));
|
||||||
element.setAttribute('download', frm.docname + '.xml');
|
element.setAttribute('download', frm.docname + '.xml');
|
||||||
|
|
@ -59,5 +60,71 @@ frappe.ui.form.on('Single declaration related to salaried and non-salaried work'
|
||||||
});
|
});
|
||||||
}, __('Actions'));
|
}, __('Actions'));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function reload_hisse_1_data(frm) {
|
||||||
|
if (!frm.doc.il || !frm.doc.hesabatdövrü) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const company = frappe.defaults.get_user_default('Company')
|
||||||
|
|| frappe.defaults.get_default('Company');
|
||||||
|
if (!company) {
|
||||||
|
frappe.msgprint(__('Company not found in user defaults'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.dom.freeze(__('Loading payroll data...'));
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: 'taxes_az.taxes_az.doctype.single_declaration_related_to_salaried_and_non_salaried_work.single_declaration_related_to_salaried_and_non_salaried_work.populate_declaration_tables',
|
||||||
|
args: {
|
||||||
|
company: company,
|
||||||
|
year: frm.doc.il,
|
||||||
|
quarter: frm.doc.hesabatdövrü
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
|
|
||||||
|
if (r.message && r.message.success) {
|
||||||
|
populate_hisse_1(frm, r.message.hisse_1_data || []);
|
||||||
|
frm.dirty();
|
||||||
|
frm.refresh_fields();
|
||||||
|
|
||||||
|
frappe.show_alert({
|
||||||
|
message: __('Payroll data loaded successfully'),
|
||||||
|
indicator: 'green'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Error'),
|
||||||
|
message: r.message ? r.message.error : __('Failed to load data'),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Error'),
|
||||||
|
message: __('Server error while loading payroll data'),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populate_hisse_1(frm, data) {
|
||||||
|
frm.clear_table('hissə_1');
|
||||||
|
|
||||||
|
data.forEach(function(emp_data) {
|
||||||
|
let row = frm.add_child('hissə_1');
|
||||||
|
Object.keys(emp_data).forEach(function(field) {
|
||||||
|
if (emp_data[field] !== null && emp_data[field] !== undefined) {
|
||||||
|
row[field] = emp_data[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.refresh_field('hissə_1');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,206 @@
|
||||||
# Copyright (c) 2025, Jey Soft and contributors
|
# Copyright (c) 2025, Jey Soft and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import flt
|
||||||
|
|
||||||
|
from taxes_az.taxes_az.report.employee_payroll_register.employee_payroll_register import (
|
||||||
|
DEDUCTION_TYPES,
|
||||||
|
EARNING_TYPES,
|
||||||
|
get_employee_doj_map,
|
||||||
|
get_salary_slip_details,
|
||||||
|
get_salary_slips,
|
||||||
|
)
|
||||||
|
|
||||||
|
QUARTER_DATE_RANGES = {
|
||||||
|
"1. Rüb": ("01-01", "03-31"),
|
||||||
|
"2. Rüb": ("04-01", "06-30"),
|
||||||
|
"3. Rüb": ("07-01", "09-30"),
|
||||||
|
"4. Rüb": ("10-01", "12-31"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mapping from frappe.scrub(salary_component_name) to child table fieldnames.
|
||||||
|
# Uses frappe.scrub() at module level to handle İ→i̇ Unicode correctly.
|
||||||
|
REPORT_KEY_TO_FIELDNAME = {
|
||||||
|
# Earnings
|
||||||
|
frappe.scrub("DSMF (xərc şirkət)"): "dsmfxərcşirkət",
|
||||||
|
frappe.scrub("Məzuniyyət Kompensasiyası"): "məzuniyyətkompensasiyası",
|
||||||
|
frappe.scrub("İcbari tibbi sığorta (xərc şirkət 2%)"): "icbaritibbisığortaxərcşirkət2",
|
||||||
|
frappe.scrub("İşsizlikdən sığorta haqqı (xərc şirkət 0,5%)"): "işsizlikdənsığortahaqqıxərcşirkət05",
|
||||||
|
frappe.scrub("Əsas əmək haqqı"): "əsasəməkhaqqı",
|
||||||
|
# Deductions
|
||||||
|
frappe.scrub("DSMF (işçi)"): "dsmfişçi",
|
||||||
|
frappe.scrub("DSMF (şirkət)"): "dsmfşirkət",
|
||||||
|
frappe.scrub("İcbari tibbi sığorta (işçi 2%)"): "icbaritibbisığortaişçi2",
|
||||||
|
frappe.scrub("İcbari tibbi sığorta (şirkət 2%)"): "icbaritibbisığortaşirkət2",
|
||||||
|
frappe.scrub("İşsizlikdən sığorta haqqı (işçi 0,5%)"): "işsizlikdənsığortahaqqıişçi05",
|
||||||
|
frappe.scrub("İşsizlikdən sığorta haqqı (şirkət 0,5%)"): "işsizlikdənsığortahaqqışirkət05",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Scrubbed keys for insurance formula calculation
|
||||||
|
_DSMF_ISCI = frappe.scrub("DSMF (işçi)")
|
||||||
|
_DSMF_SIRKET = frappe.scrub("DSMF (şirkət)")
|
||||||
|
_ISSIZLIK_ISCI = frappe.scrub("İşsizlikdən sığorta haqqı (işçi 0,5%)")
|
||||||
|
_ISSIZLIK_SIRKET = frappe.scrub("İşsizlikdən sığorta haqqı (şirkət 0,5%)")
|
||||||
|
_ITS_ISCI = frappe.scrub("İcbari tibbi sığorta (işçi 2%)")
|
||||||
|
_ITS_SIRKET = frappe.scrub("İcbari tibbi sığorta (şirkət 2%)")
|
||||||
|
|
||||||
|
|
||||||
class Singledeclarationrelatedtosalariedandnonsalariedwork(Document):
|
class Singledeclarationrelatedtosalariedandnonsalariedwork(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def populate_declaration_tables(company, year, quarter):
|
||||||
|
"""Populate child tables from Employee Payroll Register data.
|
||||||
|
|
||||||
|
Returns dict with data for each child table.
|
||||||
|
Designed for extensibility: future tables add their key to the result dict.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
year = int(year)
|
||||||
|
if quarter not in QUARTER_DATE_RANGES:
|
||||||
|
return {"success": False, "error": f"Invalid quarter: {quarter}"}
|
||||||
|
|
||||||
|
start_suffix, end_suffix = QUARTER_DATE_RANGES[quarter]
|
||||||
|
from_date = f"{year}-{start_suffix}"
|
||||||
|
to_date = f"{year}-{end_suffix}"
|
||||||
|
|
||||||
|
hisse_1_data = get_aggregated_employee_data(company, from_date, to_date)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"hisse_1_data": hisse_1_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(
|
||||||
|
f"Error in populate_declaration_tables: {str(e)}",
|
||||||
|
"Single Declaration",
|
||||||
|
)
|
||||||
|
return {"success": False, "error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_aggregated_employee_data(company, from_date, to_date):
|
||||||
|
"""Fetch salary slip data and aggregate per employee for hissə_1."""
|
||||||
|
import erpnext
|
||||||
|
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
currency = "AZN"
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
"company": company,
|
||||||
|
"from_date": from_date,
|
||||||
|
"to_date": to_date,
|
||||||
|
"currency": currency,
|
||||||
|
"docstatus": "Submitted",
|
||||||
|
}
|
||||||
|
|
||||||
|
salary_slips = get_salary_slips(filters, company_currency)
|
||||||
|
if not salary_slips:
|
||||||
|
return []
|
||||||
|
|
||||||
|
ss_earning_map = get_salary_slip_details(salary_slips, currency, company_currency, "earnings")
|
||||||
|
ss_ded_map = get_salary_slip_details(salary_slips, currency, company_currency, "deductions")
|
||||||
|
doj_map = get_employee_doj_map()
|
||||||
|
|
||||||
|
# Build per-slip rows (same logic as report's execute())
|
||||||
|
all_scrubbed_keys = []
|
||||||
|
for e in EARNING_TYPES:
|
||||||
|
all_scrubbed_keys.append(frappe.scrub(e))
|
||||||
|
for d in DEDUCTION_TYPES:
|
||||||
|
all_scrubbed_keys.append(frappe.scrub(d))
|
||||||
|
|
||||||
|
sum_fields = [
|
||||||
|
"leave_without_pay",
|
||||||
|
"absent_days",
|
||||||
|
"payment_days",
|
||||||
|
"gross_pay",
|
||||||
|
"total_deduction",
|
||||||
|
"net_pay",
|
||||||
|
] + all_scrubbed_keys
|
||||||
|
|
||||||
|
employee_map = {}
|
||||||
|
|
||||||
|
for ss in salary_slips:
|
||||||
|
emp = ss.employee
|
||||||
|
|
||||||
|
if emp not in employee_map:
|
||||||
|
employee_map[emp] = {
|
||||||
|
"employee": emp,
|
||||||
|
"employee_name": ss.employee_name,
|
||||||
|
"date_of_joining": doj_map.get(emp),
|
||||||
|
"branch": ss.branch,
|
||||||
|
"department": ss.department,
|
||||||
|
"designation": ss.designation,
|
||||||
|
}
|
||||||
|
for field in sum_fields:
|
||||||
|
employee_map[emp][field] = 0.0
|
||||||
|
|
||||||
|
agg = employee_map[emp]
|
||||||
|
|
||||||
|
# Earnings
|
||||||
|
for e in EARNING_TYPES:
|
||||||
|
key = frappe.scrub(e)
|
||||||
|
agg[key] += flt(ss_earning_map.get(ss.name, {}).get(e, 0))
|
||||||
|
|
||||||
|
# Gross pay
|
||||||
|
if currency == company_currency:
|
||||||
|
agg["gross_pay"] += flt(ss.gross_pay) * flt(ss.exchange_rate)
|
||||||
|
else:
|
||||||
|
agg["gross_pay"] += flt(ss.gross_pay)
|
||||||
|
|
||||||
|
# Deductions
|
||||||
|
for d in DEDUCTION_TYPES:
|
||||||
|
key = frappe.scrub(d)
|
||||||
|
agg[key] += flt(ss_ded_map.get(ss.name, {}).get(d, 0))
|
||||||
|
|
||||||
|
# Totals
|
||||||
|
if currency == company_currency:
|
||||||
|
agg["total_deduction"] += flt(ss.total_deduction) * flt(ss.exchange_rate)
|
||||||
|
agg["net_pay"] += flt(ss.net_pay) * flt(ss.exchange_rate)
|
||||||
|
else:
|
||||||
|
agg["total_deduction"] += flt(ss.total_deduction)
|
||||||
|
agg["net_pay"] += flt(ss.net_pay)
|
||||||
|
|
||||||
|
# Days
|
||||||
|
agg["leave_without_pay"] += flt(ss.leave_without_pay)
|
||||||
|
agg["absent_days"] += flt(ss.absent_days)
|
||||||
|
agg["payment_days"] += flt(ss.payment_days)
|
||||||
|
|
||||||
|
# Convert to child table format with clean fieldnames
|
||||||
|
result = []
|
||||||
|
for agg in employee_map.values():
|
||||||
|
child_row = {
|
||||||
|
"employee": agg["employee"],
|
||||||
|
"employeename": agg["employee_name"],
|
||||||
|
"dateofjoining": str(agg["date_of_joining"]) if agg["date_of_joining"] else None,
|
||||||
|
"branch": agg.get("branch"),
|
||||||
|
"department": agg.get("department"),
|
||||||
|
"designation": agg.get("designation"),
|
||||||
|
"leavewithoutpay": agg["leave_without_pay"],
|
||||||
|
"absentdays": agg["absent_days"],
|
||||||
|
"paymentdays": agg["payment_days"],
|
||||||
|
"grosspay": agg["gross_pay"],
|
||||||
|
"totaldeduction": agg["total_deduction"],
|
||||||
|
"netpay": agg["net_pay"],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Map scrubbed report keys to clean child table fieldnames
|
||||||
|
for report_key, child_fieldname in REPORT_KEY_TO_FIELDNAME.items():
|
||||||
|
child_row[child_fieldname] = agg.get(report_key, 0.0)
|
||||||
|
|
||||||
|
# Calculate insurance fields: (işçi + şirkət) × 3
|
||||||
|
child_row["hesablanmışmdsshaqqı"] = (flt(agg.get(_DSMF_ISCI, 0)) + flt(agg.get(_DSMF_SIRKET, 0))) * 3
|
||||||
|
child_row["hesablanmışişsizlikdənsığortahaqqı"] = (
|
||||||
|
flt(agg.get(_ISSIZLIK_ISCI, 0)) + flt(agg.get(_ISSIZLIK_SIRKET, 0))
|
||||||
|
) * 3
|
||||||
|
child_row["hesablanmışicbaritibbisığortahaqqı"] = (
|
||||||
|
flt(agg.get(_ITS_ISCI, 0)) + flt(agg.get(_ITS_SIRKET, 0))
|
||||||
|
) * 3
|
||||||
|
|
||||||
|
result.append(child_row)
|
||||||
|
|
||||||
|
result.sort(key=lambda x: x.get("employeename", ""))
|
||||||
|
return result
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue