added win loss in sales invoice
This commit is contained in:
parent
b032b6fb85
commit
c1029e14f4
|
|
@ -125,6 +125,123 @@ def create_custom_fields():
|
||||||
insert_after='is_paid',
|
insert_after='is_paid',
|
||||||
hidden=0
|
hidden=0
|
||||||
),
|
),
|
||||||
|
# Hidden field to fetch company main activity
|
||||||
|
dict(
|
||||||
|
fieldname='company_main_activity',
|
||||||
|
label='Company Main Activity',
|
||||||
|
fieldtype='Data',
|
||||||
|
insert_after='is_taxes_doc',
|
||||||
|
hidden=1,
|
||||||
|
fetch_from='company.main_activity',
|
||||||
|
read_only=1
|
||||||
|
),
|
||||||
|
# Organizer and Seller checkboxes
|
||||||
|
dict(
|
||||||
|
fieldname='organizer',
|
||||||
|
label='Organizer',
|
||||||
|
fieldtype='Check',
|
||||||
|
insert_after='is_debit_note',
|
||||||
|
depends_on='eval:doc.company_main_activity && ["92000", "9200003", "9200004", "9200001", "9200002", "9200005"].includes(doc.company_main_activity)',
|
||||||
|
allow_on_submit=1
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
fieldname='seller',
|
||||||
|
label='Seller',
|
||||||
|
fieldtype='Check',
|
||||||
|
insert_after='organizer',
|
||||||
|
depends_on='eval:doc.company_main_activity && ["92000", "9200003", "9200004", "9200001", "9200002", "9200005"].includes(doc.company_main_activity)',
|
||||||
|
allow_on_submit=1
|
||||||
|
),
|
||||||
|
# Won and Lost checkboxes after section_break_42 (в ряд)
|
||||||
|
dict(
|
||||||
|
fieldname='won_lost_html',
|
||||||
|
fieldtype='HTML',
|
||||||
|
insert_after='section_break_42',
|
||||||
|
depends_on='eval:doc.company_main_activity && ["92000", "9200003", "9200004", "9200001", "9200002", "9200005"].includes(doc.company_main_activity)',
|
||||||
|
options="""
|
||||||
|
<div style="display: flex; gap: 20px; margin: 8px 0;">
|
||||||
|
<label style="display: flex; align-items: center; gap: 5px;">
|
||||||
|
<input type="checkbox" class="won-checkbox" checked style="margin: 0;">
|
||||||
|
<span>Won</span>
|
||||||
|
</label>
|
||||||
|
<label style="display: flex; align-items: center; gap: 5px;">
|
||||||
|
<input type="checkbox" class="lost-checkbox" style="margin: 0;">
|
||||||
|
<span>Lost</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
// Ждем полной загрузки страницы и инициализации Frappe
|
||||||
|
setTimeout(function() {
|
||||||
|
if (typeof frappe === 'undefined' || !cur_frm) {
|
||||||
|
// Если Frappe еще не готов, ждем еще
|
||||||
|
setTimeout(arguments.callee, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wonCheckbox = document.querySelector('.won-checkbox');
|
||||||
|
const lostCheckbox = document.querySelector('.lost-checkbox');
|
||||||
|
|
||||||
|
if (!wonCheckbox || !lostCheckbox) return;
|
||||||
|
|
||||||
|
// Синхронизация с текущими значениями полей
|
||||||
|
wonCheckbox.checked = cur_frm.doc.won ? true : false;
|
||||||
|
lostCheckbox.checked = cur_frm.doc.lost ? true : false;
|
||||||
|
|
||||||
|
// Обеспечиваем что всегда выбрана одна галочка
|
||||||
|
if (!wonCheckbox.checked && !lostCheckbox.checked) {
|
||||||
|
wonCheckbox.checked = true;
|
||||||
|
cur_frm.set_value('won', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
wonCheckbox.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
lostCheckbox.checked = false;
|
||||||
|
cur_frm.set_value('lost', 0);
|
||||||
|
cur_frm.set_value('won', 1);
|
||||||
|
} else {
|
||||||
|
lostCheckbox.checked = true;
|
||||||
|
cur_frm.set_value('lost', 1);
|
||||||
|
cur_frm.set_value('won', 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lostCheckbox.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
wonCheckbox.checked = false;
|
||||||
|
cur_frm.set_value('won', 0);
|
||||||
|
cur_frm.set_value('lost', 1);
|
||||||
|
} else {
|
||||||
|
wonCheckbox.checked = true;
|
||||||
|
cur_frm.set_value('won', 1);
|
||||||
|
cur_frm.set_value('lost', 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 1000); // Даем время на инициализацию Frappe
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
# Скрытые поля для хранения значений
|
||||||
|
dict(
|
||||||
|
fieldname='won',
|
||||||
|
label='Won',
|
||||||
|
fieldtype='Check',
|
||||||
|
insert_after='won_lost_html',
|
||||||
|
hidden=1,
|
||||||
|
default=1,
|
||||||
|
allow_on_submit=1
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
fieldname='lost',
|
||||||
|
label='Lost',
|
||||||
|
fieldtype='Check',
|
||||||
|
insert_after='won',
|
||||||
|
hidden=1,
|
||||||
|
default=0,
|
||||||
|
allow_on_submit=1
|
||||||
|
),
|
||||||
# New comments section after time_sheet_list
|
# New comments section after time_sheet_list
|
||||||
dict(
|
dict(
|
||||||
fieldname='comments_section',
|
fieldname='comments_section',
|
||||||
|
|
@ -789,24 +906,30 @@ def create_custom_fields():
|
||||||
|
|
||||||
for doctype, fields in custom_fields.items():
|
for doctype, fields in custom_fields.items():
|
||||||
for field in fields:
|
for field in fields:
|
||||||
# Check if fieldname exists before processing
|
# Проверяем наличие fieldname перед обработкой
|
||||||
if 'fieldname' not in field:
|
if 'fieldname' not in field:
|
||||||
print(f"Skipping field without fieldname in {doctype}: {field}")
|
print(f"Skipping field without fieldname in {doctype}: {field}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check if field already exists
|
# Удаление существующего поля, если оно уже есть
|
||||||
if frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": field['fieldname']}):
|
if frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": field['fieldname']}):
|
||||||
print(f"Field {field['fieldname']} already exists in {doctype}, skipping...")
|
try:
|
||||||
continue
|
custom_field_name = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": field['fieldname']}, "name")
|
||||||
|
frappe.delete_doc("Custom Field", custom_field_name)
|
||||||
|
frappe.db.commit()
|
||||||
|
print(f"Deleted field {field['fieldname']} in {doctype}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to delete field {field['fieldname']} in {doctype}: {e}")
|
||||||
|
|
||||||
# Create field only if it doesn't exist yet
|
# Создание нового поля
|
||||||
try:
|
try:
|
||||||
create_custom_field(doctype, field)
|
create_custom_field(doctype, field)
|
||||||
print(f"Created field {field['fieldname']} in {doctype}")
|
print(f"Created field {field['fieldname']} in {doctype}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to create field {field['fieldname']} in {doctype}: {e}")
|
print(f"Failed to create field {field['fieldname']} in {doctype}: {e}")
|
||||||
|
|
||||||
# Register the calculation hook for existing Sales Invoices
|
# Коммит всех изменений
|
||||||
|
frappe.db.commit()
|
||||||
print("Setting up VAT calculation hooks...")
|
print("Setting up VAT calculation hooks...")
|
||||||
|
|
||||||
create_custom_fields()
|
create_custom_fields()
|
||||||
|
|
@ -1,2 +1,53 @@
|
||||||
// Запускаем логику калькулятора НДС для Sales Invoice
|
// Запускаем логику калькулятора НДС для Sales Invoice
|
||||||
jey_erp.vat_calculator.init('Sales Invoice');
|
jey_erp.vat_calculator.init('Sales Invoice');
|
||||||
|
|
||||||
|
// Объединенная логика для Sales Invoice
|
||||||
|
frappe.ui.form.on('Sales Invoice', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
// Убедимся что одна из галочек won/lost всегда выбрана при загрузке формы
|
||||||
|
if (!frm.doc.won && !frm.doc.lost) {
|
||||||
|
frm.set_value('won', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Убедимся что хотя бы одна из галочек organizer/seller всегда выбрана при загрузке формы
|
||||||
|
if (!frm.doc.organizer && !frm.doc.seller) {
|
||||||
|
frm.set_value('organizer', 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
won: function(frm) {
|
||||||
|
if (frm.doc.won) {
|
||||||
|
// Если Won выбрана, снимаем Lost
|
||||||
|
frm.set_value('lost', 0);
|
||||||
|
} else {
|
||||||
|
// Если Won снята, автоматически ставим Lost
|
||||||
|
frm.set_value('lost', 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
lost: function(frm) {
|
||||||
|
if (frm.doc.lost) {
|
||||||
|
// Если Lost выбрана, снимаем Won
|
||||||
|
frm.set_value('won', 0);
|
||||||
|
} else {
|
||||||
|
// Если Lost снята, автоматически ставим Won
|
||||||
|
frm.set_value('won', 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
organizer: function(frm) {
|
||||||
|
// Если пытаются выключить organizer, но seller уже выключена - не позволяем
|
||||||
|
if (!frm.doc.organizer && !frm.doc.seller) {
|
||||||
|
frappe.msgprint(__('At least one option (Organizer or Seller) must be selected'));
|
||||||
|
frm.set_value('organizer', 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
seller: function(frm) {
|
||||||
|
// Если пытаются выключить seller, но organizer уже выключена - не позволяем
|
||||||
|
if (!frm.doc.seller && !frm.doc.organizer) {
|
||||||
|
frappe.msgprint(__('At least one option (Organizer or Seller) must be selected'));
|
||||||
|
frm.set_value('seller', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue