added assets field
This commit is contained in:
parent
159acc2563
commit
b284108c0c
|
|
@ -0,0 +1,35 @@
|
|||
import frappe
|
||||
from frappe import _
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_tax_article_from_asset(tax_article_name, date_from=None, date_to=None, duration=None):
|
||||
"""
|
||||
Update Tax Article validity fields from Asset
|
||||
Called when Asset is saved
|
||||
"""
|
||||
try:
|
||||
tax_article = frappe.get_doc('Tax Article', tax_article_name)
|
||||
|
||||
if date_from is not None:
|
||||
tax_article.date_from = date_from
|
||||
|
||||
if date_to is not None:
|
||||
tax_article.date_to = date_to
|
||||
|
||||
if duration is not None:
|
||||
tax_article.duration = int(duration) if duration else None
|
||||
|
||||
tax_article.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'Tax Article {tax_article_name} updated successfully'
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error updating Tax Article: {str(e)}", "Update Tax Article Error")
|
||||
return {
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}
|
||||
|
|
@ -113,12 +113,29 @@ def create_custom_fields():
|
|||
reqd=0
|
||||
),
|
||||
|
||||
# Agricultural Validity Section
|
||||
dict(
|
||||
fieldname='agricultural_validity_section',
|
||||
label='Tax Validity Period',
|
||||
fieldtype='Section Break',
|
||||
insert_after='agricultural_tax_article',
|
||||
collapsible=1,
|
||||
depends_on='eval:doc.agricultural_tax_article'
|
||||
),
|
||||
|
||||
dict(
|
||||
fieldname='agricultural_validity_html',
|
||||
fieldtype='HTML',
|
||||
insert_after='agricultural_validity_section',
|
||||
depends_on='eval:doc.agricultural_tax_article'
|
||||
),
|
||||
|
||||
# === INDUSTRIAL/COMMERCIAL LAND SECTION ===
|
||||
dict(
|
||||
fieldname='industrial_land_section',
|
||||
label='Industrial/Commercial Land Information',
|
||||
fieldtype='Section Break',
|
||||
insert_after='agricultural_tax_article',
|
||||
insert_after='agricultural_validity_html',
|
||||
collapsible=1,
|
||||
depends_on='eval:doc.custom_asset_type == "Sənaye, tikinti, nəqliyyat, rabitə, ticarət-məişət xidməti və digər xüsusi təyinatlı torpaq sahələri"'
|
||||
),
|
||||
|
|
@ -177,13 +194,30 @@ def create_custom_fields():
|
|||
depends_on='eval:doc.custom_asset_type == "Sənaye, tikinti, nəqliyyat, rabitə, ticarət-məişət xidməti və digər xüsusi təyinatlı torpaq sahələri"',
|
||||
reqd=0
|
||||
),
|
||||
|
||||
# Industrial Validity Section
|
||||
dict(
|
||||
fieldname='industrial_validity_section',
|
||||
label='Tax Validity Period',
|
||||
fieldtype='Section Break',
|
||||
insert_after='industrial_tax_article',
|
||||
collapsible=1,
|
||||
depends_on='eval:doc.industrial_tax_article'
|
||||
),
|
||||
|
||||
dict(
|
||||
fieldname='industrial_validity_html',
|
||||
fieldtype='HTML',
|
||||
insert_after='industrial_validity_section',
|
||||
depends_on='eval:doc.industrial_tax_article'
|
||||
),
|
||||
|
||||
# === TAX EXEMPT ASSETS SECTION ===
|
||||
dict(
|
||||
fieldname='tax_exempt_assets_section',
|
||||
label='Tax Exempt Assets Information',
|
||||
fieldtype='Section Break',
|
||||
insert_after='industrial_tax_article',
|
||||
insert_after='industrial_validity_html',
|
||||
collapsible=1,
|
||||
depends_on='eval:doc.custom_asset_type == "Vergidən azad olunan əsas vəsaitlər"'
|
||||
),
|
||||
|
|
@ -197,13 +231,30 @@ def create_custom_fields():
|
|||
depends_on='eval:doc.custom_asset_type == "Vergidən azad olunan əsas vəsaitlər"',
|
||||
reqd=0
|
||||
),
|
||||
|
||||
# Tax Exempt Validity Section
|
||||
dict(
|
||||
fieldname='tax_exempt_validity_section',
|
||||
label='Tax Validity Period',
|
||||
fieldtype='Section Break',
|
||||
insert_after='tax_exempt_tax_article',
|
||||
collapsible=1,
|
||||
depends_on='eval:doc.tax_exempt_tax_article'
|
||||
),
|
||||
|
||||
dict(
|
||||
fieldname='tax_exempt_validity_html',
|
||||
fieldtype='HTML',
|
||||
insert_after='tax_exempt_validity_section',
|
||||
depends_on='eval:doc.tax_exempt_tax_article'
|
||||
),
|
||||
|
||||
dict(
|
||||
fieldname='tax_exempt_settlements',
|
||||
label='Settlements',
|
||||
fieldtype='Select',
|
||||
options='\nBakı\nGəncə, Sumqayıt şəhərləri və Abşeron rayonu\nDigər şəhərlər, rayon mərkəzləri\nRayon tabeliyində olan şəhərlərdə, qəsəbələrdə və kəndlərdə',
|
||||
insert_after='tax_exempt_tax_article',
|
||||
insert_after='tax_exempt_validity_html',
|
||||
depends_on='eval:doc.custom_asset_type == "Vergidən azad olunan əsas vəsaitlər"',
|
||||
reqd=0
|
||||
),
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ doc_events = {
|
|||
"jey_erp.custom.sales_order_vat.set_vat_fields_permissions"
|
||||
],
|
||||
"before_save": "jey_erp.custom.sales_order_vat.set_vat_fields_permissions"
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
after_migrate = "jey_erp.hooks.after_migrate_combined"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ frappe.ui.form.on('Asset', {
|
|||
setup_asset_fields(frm);
|
||||
update_area_field_label(frm);
|
||||
|
||||
// Загружаем данные из Tax Article ТОЛЬКО при первом открытии формы
|
||||
if (!frm._tax_validity_loaded) {
|
||||
render_tax_validity_fields(frm, 'agricultural');
|
||||
render_tax_validity_fields(frm, 'industrial');
|
||||
render_tax_validity_fields(frm, 'tax_exempt');
|
||||
frm._tax_validity_loaded = true;
|
||||
}
|
||||
|
||||
if (should_show_calculation_button(frm)) {
|
||||
add_calculation_button(frm);
|
||||
}
|
||||
|
|
@ -76,6 +84,32 @@ frappe.ui.form.on('Asset', {
|
|||
toggle_calculation_button(frm);
|
||||
},
|
||||
|
||||
// При выборе Tax Article - рендерим поля
|
||||
agricultural_tax_article: function(frm) {
|
||||
frm._tax_validity_loaded = false;
|
||||
render_tax_validity_fields(frm, 'agricultural');
|
||||
},
|
||||
|
||||
industrial_tax_article: function(frm) {
|
||||
frm._tax_validity_loaded = false;
|
||||
render_tax_validity_fields(frm, 'industrial');
|
||||
},
|
||||
|
||||
tax_exempt_tax_article: function(frm) {
|
||||
frm._tax_validity_loaded = false;
|
||||
render_tax_validity_fields(frm, 'tax_exempt');
|
||||
},
|
||||
|
||||
before_save: function(frm) {
|
||||
// Собираем данные из динамических полей и отправляем на сервер
|
||||
return save_tax_validity_data(frm);
|
||||
},
|
||||
|
||||
after_save: function(frm) {
|
||||
// После успешного сохранения сбрасываем флаг
|
||||
frm._tax_validity_loaded = true;
|
||||
},
|
||||
|
||||
validate: function(frm) {
|
||||
if (!frm.doc.land && !frm.doc.property) {
|
||||
frappe.msgprint('Please select either Land or Property');
|
||||
|
|
@ -153,6 +187,290 @@ frappe.ui.form.on('Asset', {
|
|||
}
|
||||
});
|
||||
|
||||
// ===== DYNAMIC TAX VALIDITY FIELDS WITH FRAPPE CONTROLS =====
|
||||
|
||||
function render_tax_validity_fields(frm, type) {
|
||||
const tax_article_field = `${type}_tax_article`;
|
||||
const html_field = `${type}_validity_html`;
|
||||
|
||||
if (!frm.doc[tax_article_field]) {
|
||||
// Очищаем контролы и wrapper
|
||||
if (frm._tax_validity_controls && frm._tax_validity_controls[type]) {
|
||||
delete frm._tax_validity_controls[type];
|
||||
}
|
||||
if (frm.fields_dict[html_field]) {
|
||||
frm.fields_dict[html_field].$wrapper.html('');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Предотвращаем повторный рендеринг если уже рендерится
|
||||
if (frm[`_rendering_${type}`]) {
|
||||
return;
|
||||
}
|
||||
frm[`_rendering_${type}`] = true;
|
||||
|
||||
// Загружаем данные из Tax Article
|
||||
frappe.call({
|
||||
method: 'frappe.client.get',
|
||||
args: {
|
||||
doctype: 'Tax Article',
|
||||
name: frm.doc[tax_article_field]
|
||||
},
|
||||
callback: function(r) {
|
||||
frm[`_rendering_${type}`] = false;
|
||||
|
||||
if (r.message) {
|
||||
const tax_article = r.message;
|
||||
|
||||
// Очищаем старые контролы
|
||||
const $wrapper = frm.fields_dict[html_field].$wrapper;
|
||||
$wrapper.html('');
|
||||
|
||||
// Удаляем старые ссылки на контролы
|
||||
if (frm._tax_validity_controls && frm._tax_validity_controls[type]) {
|
||||
delete frm._tax_validity_controls[type];
|
||||
}
|
||||
|
||||
// Создаем контейнер с нужными классами Frappe
|
||||
const $container = $('<div class="form-section">').appendTo($wrapper);
|
||||
const $row = $('<div class="row">').appendTo($container);
|
||||
|
||||
// Создаем 3 колонки для полей
|
||||
const $col1 = $('<div class="col-sm-4">').appendTo($row);
|
||||
const $col2 = $('<div class="col-sm-4">').appendTo($row);
|
||||
const $col3 = $('<div class="col-sm-4">').appendTo($row);
|
||||
|
||||
// Флаг для отслеживания первоначальной загрузки
|
||||
frm[`_initial_load_${type}`] = true;
|
||||
|
||||
// Проверяем, есть ли уже сохраненные данные в памяти (приоритет)
|
||||
let initial_date_from = tax_article.date_from;
|
||||
let initial_duration = tax_article.duration;
|
||||
let initial_date_to = tax_article.date_to;
|
||||
|
||||
if (frm._tax_validity_data && frm._tax_validity_data[type]) {
|
||||
initial_date_from = frm._tax_validity_data[type].date_from || initial_date_from;
|
||||
initial_duration = frm._tax_validity_data[type].duration || initial_duration;
|
||||
initial_date_to = frm._tax_validity_data[type].date_to || initial_date_to;
|
||||
}
|
||||
|
||||
// Создаем Date From поле
|
||||
const date_from_field = frappe.ui.form.make_control({
|
||||
parent: $col1,
|
||||
df: {
|
||||
fieldtype: 'Date',
|
||||
label: __('Date From'),
|
||||
fieldname: `${type}_date_from_dynamic`,
|
||||
change: function() {
|
||||
if (frm[`_initial_load_${type}`]) {
|
||||
return;
|
||||
}
|
||||
handle_field_change(frm, type, 'date_from');
|
||||
}
|
||||
},
|
||||
render_input: true
|
||||
});
|
||||
date_from_field.set_value(initial_date_from);
|
||||
|
||||
// Создаем Duration поле
|
||||
const duration_field = frappe.ui.form.make_control({
|
||||
parent: $col2,
|
||||
df: {
|
||||
fieldtype: 'Int',
|
||||
label: __('Duration (Years)'),
|
||||
fieldname: `${type}_duration_dynamic`,
|
||||
change: function() {
|
||||
if (frm[`_initial_load_${type}`]) {
|
||||
return;
|
||||
}
|
||||
handle_field_change(frm, type, 'duration');
|
||||
}
|
||||
},
|
||||
render_input: true
|
||||
});
|
||||
duration_field.set_value(initial_duration);
|
||||
|
||||
// Создаем Date To поле
|
||||
const date_to_field = frappe.ui.form.make_control({
|
||||
parent: $col3,
|
||||
df: {
|
||||
fieldtype: 'Date',
|
||||
label: __('Date To'),
|
||||
fieldname: `${type}_date_to_dynamic`,
|
||||
change: function() {
|
||||
if (frm[`_initial_load_${type}`]) {
|
||||
return;
|
||||
}
|
||||
handle_field_change(frm, type, 'date_to');
|
||||
}
|
||||
},
|
||||
render_input: true
|
||||
});
|
||||
date_to_field.set_value(initial_date_to);
|
||||
|
||||
// Снимаем флаг первоначальной загрузки после небольшой задержки
|
||||
setTimeout(() => {
|
||||
frm[`_initial_load_${type}`] = false;
|
||||
}, 300);
|
||||
|
||||
// Добавляем информационное сообщение
|
||||
$('<div class="text-muted small" style="margin-top: 10px; padding-left: 15px;">')
|
||||
.text(__('Changes will be saved to Tax Article when you save this Asset'))
|
||||
.appendTo($container);
|
||||
|
||||
// Сохраняем ссылки на контролы
|
||||
if (!frm._tax_validity_controls) {
|
||||
frm._tax_validity_controls = {};
|
||||
}
|
||||
frm._tax_validity_controls[type] = {
|
||||
date_from: date_from_field,
|
||||
duration: duration_field,
|
||||
date_to: date_to_field
|
||||
};
|
||||
|
||||
// Сохраняем текущие значения
|
||||
if (!frm._tax_validity_data) {
|
||||
frm._tax_validity_data = {};
|
||||
}
|
||||
frm._tax_validity_data[type] = {
|
||||
date_from: initial_date_from,
|
||||
date_to: initial_date_to,
|
||||
duration: initial_duration
|
||||
};
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
frm[`_rendering_${type}`] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handle_field_change(frm, type, field_type) {
|
||||
// Предотвращаем рекурсивные вызовы
|
||||
if (frm[`_updating_${type}`]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!frm._tax_validity_controls || !frm._tax_validity_controls[type]) {
|
||||
return;
|
||||
}
|
||||
|
||||
frm[`_updating_${type}`] = true;
|
||||
|
||||
try {
|
||||
const controls = frm._tax_validity_controls[type];
|
||||
|
||||
let date_from = controls.date_from.get_value();
|
||||
let date_to = controls.date_to.get_value();
|
||||
let duration = controls.duration.get_value();
|
||||
|
||||
// Автоматический расчет
|
||||
if (field_type === 'date_from') {
|
||||
if (date_from && duration) {
|
||||
let from_date = frappe.datetime.str_to_obj(date_from);
|
||||
let to_date = new Date(from_date);
|
||||
to_date.setFullYear(to_date.getFullYear() + duration);
|
||||
date_to = frappe.datetime.obj_to_str(to_date);
|
||||
controls.date_to.set_value(date_to);
|
||||
}
|
||||
} else if (field_type === 'duration') {
|
||||
if (date_from && duration) {
|
||||
let from_date = frappe.datetime.str_to_obj(date_from);
|
||||
let to_date = new Date(from_date);
|
||||
to_date.setFullYear(to_date.getFullYear() + duration);
|
||||
date_to = frappe.datetime.obj_to_str(to_date);
|
||||
controls.date_to.set_value(date_to);
|
||||
}
|
||||
} else if (field_type === 'date_to') {
|
||||
if (date_from && date_to) {
|
||||
let from_date = frappe.datetime.str_to_obj(date_from);
|
||||
let to_date_obj = frappe.datetime.str_to_obj(date_to);
|
||||
|
||||
if (to_date_obj < from_date) {
|
||||
frappe.msgprint({
|
||||
title: __('Invalid Date Range'),
|
||||
indicator: 'red',
|
||||
message: __('Date To cannot be earlier than Date From')
|
||||
});
|
||||
controls.date_to.set_value('');
|
||||
frm[`_updating_${type}`] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let years = (to_date_obj - from_date) / (365.25 * 24 * 60 * 60 * 1000);
|
||||
duration = Math.round(years);
|
||||
controls.duration.set_value(duration);
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем локальные переменные (уже пересчитанные значения)
|
||||
if (!frm._tax_validity_data) {
|
||||
frm._tax_validity_data = {};
|
||||
}
|
||||
frm._tax_validity_data[type] = {
|
||||
date_from: date_from,
|
||||
date_to: date_to,
|
||||
duration: duration
|
||||
};
|
||||
|
||||
frm.dirty();
|
||||
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
frm[`_updating_${type}`] = false;
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function save_tax_validity_data(frm) {
|
||||
if (!frm._tax_validity_data) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
|
||||
// Для каждого типа Tax Article
|
||||
['agricultural', 'industrial', 'tax_exempt'].forEach(type => {
|
||||
const tax_article_field = `${type}_tax_article`;
|
||||
|
||||
if (frm.doc[tax_article_field] && frm._tax_validity_data[type]) {
|
||||
const data = frm._tax_validity_data[type];
|
||||
|
||||
promises.push(
|
||||
frappe.call({
|
||||
method: 'jey_erp.asset_events.update_tax_article_from_asset',
|
||||
args: {
|
||||
tax_article_name: frm.doc[tax_article_field],
|
||||
date_from: data.date_from,
|
||||
date_to: data.date_to,
|
||||
duration: data.duration
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (promises.length > 0) {
|
||||
return Promise.all(promises).then(() => {
|
||||
frappe.show_alert({
|
||||
message: __('Tax Article(s) updated successfully'),
|
||||
indicator: 'green'
|
||||
}, 3);
|
||||
}).catch(() => {
|
||||
frappe.msgprint({
|
||||
title: __('Error'),
|
||||
message: __('Failed to update Tax Article'),
|
||||
indicator: 'red'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// ===== ОСТАЛЬНЫЕ ФУНКЦИИ =====
|
||||
|
||||
function update_area_field_label(frm) {
|
||||
let asset_type = frm.doc.custom_asset_type;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue