mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-14 18:51:21 +00:00
Merge branch 'hotfix' into fix_bundlestock
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '11.1.28'
|
__version__ = '11.1.30'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -42,15 +42,14 @@ frappe.ui.form.on('Account', {
|
|||||||
// show / hide convert buttons
|
// show / hide convert buttons
|
||||||
frm.trigger('add_toolbar_buttons');
|
frm.trigger('add_toolbar_buttons');
|
||||||
}
|
}
|
||||||
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
if (frm.has_perm('write')) {
|
||||||
frm.trigger("update_account_number");
|
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
||||||
});
|
frm.trigger("update_account_number");
|
||||||
}
|
});
|
||||||
|
frm.add_custom_button(__('Merge Account'), function () {
|
||||||
if(!frm.doc.__islocal) {
|
frm.trigger("merge_account");
|
||||||
frm.add_custom_button(__('Merge Account'), function () {
|
});
|
||||||
frm.trigger("merge_account");
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
account_type: function (frm) {
|
account_type: function (frm) {
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ def update_account_number(name, account_name, account_number=None):
|
|||||||
|
|
||||||
new_name = get_account_autoname(account_number, account_name, account.company)
|
new_name = get_account_autoname(account_number, account_name, account.company)
|
||||||
if name != new_name:
|
if name != new_name:
|
||||||
frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
|
frappe.rename_doc("Account", name, new_name, force=1)
|
||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -287,7 +287,7 @@ def merge_account(old, new, is_group, root_type, company):
|
|||||||
frappe.db.set_value("Account", new, "parent_account",
|
frappe.db.set_value("Account", new, "parent_account",
|
||||||
frappe.db.get_value("Account", old, "parent_account"))
|
frappe.db.get_value("Account", old, "parent_account"))
|
||||||
|
|
||||||
frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
|
frappe.rename_doc("Account", old, new, merge=1, force=1)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,5 @@ class BankGuarantee(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_vouchar_detials(column_list, doctype, docname):
|
def get_vouchar_detials(column_list, doctype, docname):
|
||||||
print (column_list, doctype, docname)
|
|
||||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Settled",
|
"default": "Pending",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@@ -755,7 +755,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-26 14:32:16.437813",
|
"modified": "2019-05-11 05:27:55.244721",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Transaction",
|
"name": "Bank Transaction",
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ class BankTransaction(StatusUpdater):
|
|||||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
|
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
|
||||||
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
|
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
|
||||||
|
|
||||||
|
amount = self.debit or self.credit
|
||||||
|
if amount == self.allocated_amount:
|
||||||
|
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
||||||
|
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
def clear_linked_payment_entries(self):
|
def clear_linked_payment_entries(self):
|
||||||
|
|||||||
@@ -167,7 +167,12 @@ def get_pricing_rule_for_item(args):
|
|||||||
|
|
||||||
if args.transaction_type=="selling":
|
if args.transaction_type=="selling":
|
||||||
if args.customer and not (args.customer_group and args.territory):
|
if args.customer and not (args.customer_group and args.territory):
|
||||||
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
|
|
||||||
|
if args.quotation_to and args.quotation_to != 'Customer':
|
||||||
|
customer = frappe._dict()
|
||||||
|
else:
|
||||||
|
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
|
||||||
|
|
||||||
if customer:
|
if customer:
|
||||||
args.customer_group, args.territory = customer
|
args.customer_group, args.territory = customer
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.validate_fixed_asset()
|
self.validate_fixed_asset()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||||
|
|
||||||
def validate_release_date(self):
|
def validate_release_date(self):
|
||||||
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
def update_status_updater_args(self):
|
def update_status_updater_args(self):
|
||||||
if cint(self.update_stock):
|
if cint(self.update_stock):
|
||||||
self.status_updater.extend([{
|
self.status_updater.append({
|
||||||
'source_dt': 'Purchase Invoice Item',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
'join_field': 'po_detail',
|
'join_field': 'po_detail',
|
||||||
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
|
|||||||
'target_parent_dt': 'Purchase Order',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'target_parent_field': 'per_received',
|
'target_parent_field': 'per_received',
|
||||||
'target_ref_field': 'qty',
|
'target_ref_field': 'qty',
|
||||||
'source_field': 'qty',
|
'source_field': 'received_qty',
|
||||||
|
'second_source_dt': 'Purchase Receipt Item',
|
||||||
|
'second_source_field': 'received_qty',
|
||||||
|
'second_join_field': 'purchase_order_item',
|
||||||
'percent_join_field':'purchase_order',
|
'percent_join_field':'purchase_order',
|
||||||
# 'percent_join_field': 'prevdoc_docname',
|
|
||||||
'overflow_type': 'receipt',
|
'overflow_type': 'receipt',
|
||||||
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||||
},
|
})
|
||||||
{
|
if cint(self.is_return):
|
||||||
'source_dt': 'Purchase Invoice Item',
|
self.status_updater.append({
|
||||||
'target_dt': 'Purchase Order Item',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'join_field': 'po_detail',
|
'target_dt': 'Purchase Order Item',
|
||||||
'target_field': 'returned_qty',
|
'join_field': 'po_detail',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'target_field': 'returned_qty',
|
||||||
# 'target_parent_field': 'per_received',
|
'source_field': '-1 * qty',
|
||||||
# 'target_ref_field': 'qty',
|
'second_source_dt': 'Purchase Receipt Item',
|
||||||
'source_field': '-1 * qty',
|
'second_source_field': '-1 * qty',
|
||||||
# 'percent_join_field': 'prevdoc_docname',
|
'second_join_field': 'purchase_order_item',
|
||||||
# 'overflow_type': 'receipt',
|
'overflow_type': 'receipt',
|
||||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
|
||||||
def validate_purchase_receipt_if_update_stock(self):
|
def validate_purchase_receipt_if_update_stock(self):
|
||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.check_prev_docstatus()
|
self.check_prev_docstatus()
|
||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
self.company, self.base_grand_total)
|
self.company, self.base_grand_total)
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
self.update_prevdoc_status()
|
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
@@ -763,13 +765,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.check_for_closed_status()
|
self.check_for_closed_status()
|
||||||
|
|
||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||||
unlink_ref_doc_from_payment_entries(self)
|
unlink_ref_doc_from_payment_entries(self)
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
|
|||||||
@@ -402,9 +402,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi.save()
|
pi.save()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 756.15)
|
self.assertEqual(pi.payment_schedule[0].payment_amount, 606.15)
|
||||||
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
|
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
|
||||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 756.15)
|
self.assertEqual(pi.payment_schedule[1].payment_amount, 606.15)
|
||||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
|
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
|
||||||
|
|
||||||
pi.load_from_db()
|
pi.load_from_db()
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def update_status_updater_args(self):
|
def update_status_updater_args(self):
|
||||||
if cint(self.update_stock):
|
if cint(self.update_stock):
|
||||||
self.status_updater.extend([{
|
self.status_updater.append({
|
||||||
'source_dt':'Sales Invoice Item',
|
'source_dt':'Sales Invoice Item',
|
||||||
'target_dt':'Sales Order Item',
|
'target_dt':'Sales Order Item',
|
||||||
'target_parent_dt':'Sales Order',
|
'target_parent_dt':'Sales Order',
|
||||||
@@ -274,21 +274,20 @@ class SalesInvoice(SellingController):
|
|||||||
'overflow_type': 'delivery',
|
'overflow_type': 'delivery',
|
||||||
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
||||||
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
||||||
},
|
})
|
||||||
{
|
if cint(self.is_return):
|
||||||
'source_dt': 'Sales Invoice Item',
|
self.status_updater.append({
|
||||||
'target_dt': 'Sales Order Item',
|
'source_dt': 'Sales Invoice Item',
|
||||||
'join_field': 'so_detail',
|
'target_dt': 'Sales Order Item',
|
||||||
'target_field': 'returned_qty',
|
'join_field': 'so_detail',
|
||||||
'target_parent_dt': 'Sales Order',
|
'target_field': 'returned_qty',
|
||||||
# 'target_parent_field': 'per_delivered',
|
'target_parent_dt': 'Sales Order',
|
||||||
# 'target_ref_field': 'qty',
|
'source_field': '-1 * qty',
|
||||||
'source_field': '-1 * qty',
|
'second_source_dt': 'Delivery Note Item',
|
||||||
# 'percent_join_field': 'sales_order',
|
'second_source_field': '-1 * qty',
|
||||||
# 'overflow_type': 'delivery',
|
'second_join_field': 'so_detail',
|
||||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
|
||||||
def check_credit_limit(self):
|
def check_credit_limit(self):
|
||||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||||
@@ -508,8 +507,8 @@ class SalesInvoice(SellingController):
|
|||||||
for i in dic:
|
for i in dic:
|
||||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1 \
|
if (d.item_code and frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1
|
||||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1]):
|
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ def add_payment_to_transaction(transaction, payment_entry, gl_entry):
|
|||||||
"payment_entry": payment_entry.name,
|
"payment_entry": payment_entry.name,
|
||||||
"allocated_amount": allocated_amount
|
"allocated_amount": allocated_amount
|
||||||
})
|
})
|
||||||
|
|
||||||
transaction.save()
|
transaction.save()
|
||||||
|
transaction.update_allocations()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_linked_payments(bank_transaction):
|
def get_linked_payments(bank_transaction):
|
||||||
@@ -56,7 +58,11 @@ def get_linked_payments(bank_transaction):
|
|||||||
return check_amount_vs_description(amount_matching, description_matching)
|
return check_amount_vs_description(amount_matching, description_matching)
|
||||||
|
|
||||||
elif description_matching:
|
elif description_matching:
|
||||||
return sorted(description_matching, key = lambda x: x["posting_date"], reverse=True)
|
description_matching = filter(lambda x: not x.get('clearance_date'), description_matching)
|
||||||
|
if not description_matching:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
@@ -97,7 +103,8 @@ def check_matching_amount(bank_account, company, transaction):
|
|||||||
journal_entries = frappe.db.sql("""
|
journal_entries = frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
|
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
|
||||||
je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.credit_in_account_currency as paid_amount
|
jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date,
|
||||||
|
jea.credit_in_account_currency as paid_amount
|
||||||
FROM
|
FROM
|
||||||
`tabJournal Entry Account` as jea
|
`tabJournal Entry Account` as jea
|
||||||
JOIN
|
JOIN
|
||||||
@@ -107,12 +114,17 @@ def check_matching_amount(bank_account, company, transaction):
|
|||||||
WHERE
|
WHERE
|
||||||
(je.clearance_date is null or je.clearance_date='0000-00-00')
|
(je.clearance_date is null or je.clearance_date='0000-00-00')
|
||||||
AND
|
AND
|
||||||
jea.account = %s
|
jea.account = %(bank_account)s
|
||||||
AND
|
AND
|
||||||
jea.credit_in_account_currency like %s
|
jea.credit_in_account_currency like %(txt)s
|
||||||
AND
|
AND
|
||||||
je.docstatus = 1
|
je.docstatus = 1
|
||||||
""", (bank_account, amount), as_dict=True)
|
""", {
|
||||||
|
'bank_account': bank_account,
|
||||||
|
'txt': '%%%s%%' % amount
|
||||||
|
}, as_dict=True)
|
||||||
|
|
||||||
|
frappe.errprint(journal_entries)
|
||||||
|
|
||||||
if transaction.credit > 0:
|
if transaction.credit > 0:
|
||||||
sales_invoices = frappe.db.sql("""
|
sales_invoices = frappe.db.sql("""
|
||||||
@@ -213,9 +225,14 @@ def get_matching_descriptions_data(company, transaction):
|
|||||||
company_currency = get_company_currency(company)
|
company_currency = get_company_currency(company)
|
||||||
for key, value in iteritems(links):
|
for key, value in iteritems(links):
|
||||||
if key == "Payment Entry":
|
if key == "Payment Entry":
|
||||||
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", "reference_date", "paid_amount", "paid_to_account_currency as currency"]))
|
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]],
|
||||||
|
fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no",
|
||||||
|
"reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"]))
|
||||||
if key == "Journal Entry":
|
if key == "Journal Entry":
|
||||||
journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]], fields=["name", "'Journal Entry' as doctype", "posting_date", "pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date", "total_credit as paid_amount"])
|
journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]],
|
||||||
|
fields=["name", "'Journal Entry' as doctype", "posting_date",
|
||||||
|
"pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date",
|
||||||
|
"total_credit as paid_amount", "clearance_date"])
|
||||||
for journal_entry in journal_entries:
|
for journal_entry in journal_entries:
|
||||||
journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
|
journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
|
||||||
journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
|
journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
|
||||||
@@ -236,6 +253,9 @@ def check_amount_vs_description(amount_matching, description_matching):
|
|||||||
if description_matching:
|
if description_matching:
|
||||||
for am_match in amount_matching:
|
for am_match in amount_matching:
|
||||||
for des_match in description_matching:
|
for des_match in description_matching:
|
||||||
|
if des_match.get("clearance_date"):
|
||||||
|
continue
|
||||||
|
|
||||||
if am_match["party"] == des_match["party"]:
|
if am_match["party"] == des_match["party"]:
|
||||||
if am_match not in result:
|
if am_match not in result:
|
||||||
result.append(am_match)
|
result.append(am_match)
|
||||||
|
|||||||
@@ -111,10 +111,10 @@
|
|||||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||||
|
|
||||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||||
<th style="width: 16%">{%= __("Reference") %}</th>
|
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||||
{% } else { %}
|
{% } else { %}
|
||||||
<th style="width: 26%">{%= __("Reference") %}</th>
|
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% if(!filters.show_pdc_in_print) { %}
|
{% if(!filters.show_pdc_in_print) { %}
|
||||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||||
{% if(filters.show_pdc_in_print) { %}
|
{% if(filters.show_pdc_in_print) { %}
|
||||||
{% if(report.report_name === "Accounts Receivable") { %}
|
{% if(report.report_name === "Accounts Receivable") { %}
|
||||||
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
|
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
||||||
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
||||||
|
|||||||
@@ -321,7 +321,10 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
|||||||
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
||||||
|
|
||||||
def compare_accounts(a, b):
|
def compare_accounts(a, b):
|
||||||
if is_root:
|
if re.split('\W+', a[key])[0].isdigit():
|
||||||
|
# if chart of accounts is numbered, then sort by number
|
||||||
|
return cmp(a[key], b[key])
|
||||||
|
elif is_root:
|
||||||
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
||||||
return -1
|
return -1
|
||||||
if a.root_type != b.root_type and a.root_type == "Asset":
|
if a.root_type != b.root_type and a.root_type == "Asset":
|
||||||
@@ -330,10 +333,6 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
|||||||
return -1
|
return -1
|
||||||
if a.root_type == "Income" and b.root_type == "Expense":
|
if a.root_type == "Income" and b.root_type == "Expense":
|
||||||
return -1
|
return -1
|
||||||
else:
|
|
||||||
if re.split('\W+', a[key])[0].isdigit():
|
|
||||||
# if chart of accounts is numbered, then sort by number
|
|
||||||
return cmp(a[key], b[key])
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
frappe.query_reports["Inactive Sales Items"] = {
|
frappe.query_reports["Inactive Sales Items"] = {
|
||||||
"filters": [
|
"filters": [
|
||||||
|
{
|
||||||
|
fieldname: "territory",
|
||||||
|
label: __("Territory"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Territory",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "item",
|
fieldname: "item",
|
||||||
label: __("Item"),
|
label: __("Item"),
|
||||||
|
|||||||
@@ -7,13 +7,11 @@ from frappe.utils import getdate, add_days, today, cint
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
@@ -74,36 +72,39 @@ def get_columns():
|
|||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
items = get_items(filters)
|
items = get_items(filters)
|
||||||
|
territories = get_territories(filters)
|
||||||
sales_invoice_data = get_sales_details(filters)
|
sales_invoice_data = get_sales_details(filters)
|
||||||
|
|
||||||
for item in items:
|
for territory in territories:
|
||||||
row = {
|
for item in items:
|
||||||
|
row = {
|
||||||
|
"territory": territory.name,
|
||||||
"item_group": item.item_group,
|
"item_group": item.item_group,
|
||||||
"item": item.name,
|
"item": item.name,
|
||||||
"item_name": item.item_name
|
"item_name": item.item_name
|
||||||
}
|
}
|
||||||
|
|
||||||
if sales_invoice_data.get(item.name):
|
if sales_invoice_data.get((territory.name,item.name)):
|
||||||
item_obj = sales_invoice_data[item.name]
|
item_obj = sales_invoice_data[(territory.name,item.name)]
|
||||||
if item_obj.days_since_last_order > cint(filters['days']):
|
if item_obj.days_since_last_order > cint(filters['days']):
|
||||||
row.update({
|
row.update({
|
||||||
"territory": item_obj.territory,
|
"territory": item_obj.territory,
|
||||||
"customer": item_obj.customer,
|
"customer": item_obj.customer,
|
||||||
"last_order_date": item_obj.last_order_date,
|
"last_order_date": item_obj.last_order_date,
|
||||||
"qty": item_obj.qty,
|
"qty": item_obj.qty,
|
||||||
"days_since_last_order": item_obj.days_since_last_order
|
"days_since_last_order": item_obj.days_since_last_order
|
||||||
})
|
})
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_sales_details(filters):
|
def get_sales_details(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
item_details_map = {}
|
item_details_map = {}
|
||||||
|
|
||||||
@@ -118,12 +119,21 @@ def get_sales_details(filters):
|
|||||||
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
||||||
|
|
||||||
for d in sales_data:
|
for d in sales_data:
|
||||||
item_details_map.setdefault(d.item_name, d)
|
item_details_map.setdefault((d.territory,d.item_name), d)
|
||||||
|
|
||||||
return item_details_map
|
return item_details_map
|
||||||
|
|
||||||
def get_items(filters):
|
def get_territories(filters):
|
||||||
|
|
||||||
|
filter_dict = {}
|
||||||
|
if filters.get("territory"):
|
||||||
|
filter_dict.update({'name': filters['territory']})
|
||||||
|
|
||||||
|
territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
|
||||||
|
|
||||||
|
return territories
|
||||||
|
|
||||||
|
def get_items(filters):
|
||||||
filters_dict = {
|
filters_dict = {
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"is_stock_item": 1
|
"is_stock_item": 1
|
||||||
|
|||||||
@@ -108,6 +108,69 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||||
|
|
||||||
|
def test_update_qty(self):
|
||||||
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
make_pr_against_po(po.name, 6)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
# Check received_qty after make_purchase_invoice without update_stock checked
|
||||||
|
pi1 = make_purchase_invoice(po.name)
|
||||||
|
pi1.get("items")[0].qty = 6
|
||||||
|
pi1.insert()
|
||||||
|
pi1.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
# Check received_qty after make_purchase_invoice with update_stock checked
|
||||||
|
pi2 = make_purchase_invoice(po.name)
|
||||||
|
pi2.set("update_stock", 1)
|
||||||
|
pi2.get("items")[0].qty = 3
|
||||||
|
pi2.insert()
|
||||||
|
pi2.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||||
|
|
||||||
|
def test_return_against_purchase_order(self):
|
||||||
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
pr = make_pr_against_po(po.name, 6)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
pi2 = make_purchase_invoice(po.name)
|
||||||
|
pi2.set("update_stock", 1)
|
||||||
|
pi2.get("items")[0].qty = 3
|
||||||
|
pi2.insert()
|
||||||
|
pi2.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||||
|
|
||||||
|
# Make return purchase receipt, purchase invoice and check quantity
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||||
|
import make_purchase_receipt as make_purchase_receipt_return
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||||
|
import make_purchase_invoice as make_purchase_invoice_return
|
||||||
|
|
||||||
|
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
|
||||||
|
pr1.items[0].purchase_order = po.name
|
||||||
|
pr1.items[0].purchase_order_item = po.items[0].name
|
||||||
|
pr1.submit()
|
||||||
|
|
||||||
|
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||||
|
pi1.items[0].purchase_order = po.name
|
||||||
|
pi1.items[0].po_detail = po.items[0].name
|
||||||
|
pi1.submit()
|
||||||
|
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||||
|
|
||||||
def test_make_purchase_invoice(self):
|
def test_make_purchase_invoice(self):
|
||||||
po = create_purchase_order(do_not_submit=True)
|
po = create_purchase_order(do_not_submit=True)
|
||||||
@@ -469,6 +532,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEquals(se_items, supplied_items)
|
self.assertEquals(se_items, supplied_items)
|
||||||
update_backflush_based_on("BOM")
|
update_backflush_based_on("BOM")
|
||||||
|
|
||||||
|
def make_pr_against_po(po, received_qty=0):
|
||||||
|
pr = make_purchase_receipt(po)
|
||||||
|
pr.get("items")[0].qty = received_qty or 5
|
||||||
|
pr.insert()
|
||||||
|
pr.submit()
|
||||||
|
return pr
|
||||||
|
|
||||||
def make_subcontracted_item(item_code):
|
def make_subcontracted_item(item_code):
|
||||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,12 @@ def get_data():
|
|||||||
"name": "Customers Without Any Sales Transactions",
|
"name": "Customers Without Any Sales Transactions",
|
||||||
"doctype": "Customer"
|
"doctype": "Customer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "report",
|
||||||
|
"is_query_report": True,
|
||||||
|
"name": "Sales Partners Commission",
|
||||||
|
"doctype": "Customer"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -763,6 +763,9 @@ class AccountsController(TransactionBase):
|
|||||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
grand_total = grand_total - flt(self.write_off_amount)
|
grand_total = grand_total - flt(self.write_off_amount)
|
||||||
|
|
||||||
|
if self.get("total_advance"):
|
||||||
|
grand_total -= self.get("total_advance")
|
||||||
|
|
||||||
if not self.get("payment_schedule"):
|
if not self.get("payment_schedule"):
|
||||||
if self.get("payment_terms_template"):
|
if self.get("payment_terms_template"):
|
||||||
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
|
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
|
||||||
@@ -808,6 +811,9 @@ class AccountsController(TransactionBase):
|
|||||||
total = flt(total, self.precision("grand_total"))
|
total = flt(total, self.precision("grand_total"))
|
||||||
|
|
||||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||||
|
if self.get("total_advance"):
|
||||||
|
grand_total -= self.get("total_advance")
|
||||||
|
|
||||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
grand_total = grand_total - flt(self.write_off_amount)
|
grand_total = grand_total - flt(self.write_off_amount)
|
||||||
if total != grand_total:
|
if total != grand_total:
|
||||||
|
|||||||
@@ -254,11 +254,13 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
|||||||
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
|
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
|
||||||
""" % {
|
""" % {
|
||||||
"key": searchfield,
|
"key": searchfield,
|
||||||
"fcond": get_filters_cond(doctype, filters, []),
|
"fcond": get_filters_cond(doctype, filters, []),
|
||||||
"mcond": get_match_cond(doctype),
|
"mcond": get_match_cond(doctype),
|
||||||
|
"start": start,
|
||||||
|
"page_len": page_len,
|
||||||
"txt": "%(txt)s"
|
"txt": "%(txt)s"
|
||||||
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
|
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
|
||||||
|
|
||||||
|
|||||||
@@ -5,79 +5,79 @@ frappe.provide("erpnext");
|
|||||||
cur_frm.email_field = "email_id";
|
cur_frm.email_field = "email_id";
|
||||||
|
|
||||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||||
setup: function() {
|
setup: function () {
|
||||||
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
|
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
|
||||||
return { query: "erpnext.controllers.queries.customer_query" } }
|
return { query: "erpnext.controllers.queries.customer_query" }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function() {
|
onload: function () {
|
||||||
|
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
||||||
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
|
||||||
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
|
|
||||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||||
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
|
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
|
||||||
return { query: "frappe.core.doctype.user.user.user_query" } }
|
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function() {
|
refresh: function () {
|
||||||
var doc = this.frm.doc;
|
var doc = this.frm.doc;
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
|
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
|
||||||
|
|
||||||
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
if (!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
||||||
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
|
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
|
||||||
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
|
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
|
||||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
|
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.frm.doc.__islocal) {
|
if (!this.frm.doc.__islocal) {
|
||||||
frappe.contacts.render_address_and_contact(cur_frm);
|
frappe.contacts.render_address_and_contact(cur_frm);
|
||||||
} else {
|
} else {
|
||||||
frappe.contacts.clear_address_and_contact(cur_frm);
|
frappe.contacts.clear_address_and_contact(cur_frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
create_customer: function() {
|
create_customer: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
create_opportunity: function() {
|
create_opportunity: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
make_quotation: function() {
|
make_quotation: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
organization_lead: function() {
|
organization_lead: function () {
|
||||||
if (this.frm.doc.organization_lead == 1) {
|
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||||
this.frm.set_df_property('company_name', 'reqd', 1);
|
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
|
||||||
} else {
|
|
||||||
this.frm.set_df_property('company_name', 'reqd', 0);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
company_name: function() {
|
company_name: function () {
|
||||||
if (this.frm.doc.organization_lead == 1) {
|
if (this.frm.doc.organization_lead == 1) {
|
||||||
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
contact_date: function() {
|
contact_date: function () {
|
||||||
if (this.frm.doc.contact_date) {
|
if (this.frm.doc.contact_date) {
|
||||||
let d = moment(this.frm.doc.contact_date);
|
let d = moment(this.frm.doc.contact_date);
|
||||||
d.add(1, "hours");
|
d.add(1, "hours");
|
||||||
@@ -86,4 +86,4 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
|
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 0,
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
@@ -499,7 +499,7 @@
|
|||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Lead Owner",
|
"label": "Lead Owner",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
@@ -1389,7 +1389,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-10-02 09:12:23.415379",
|
"modified": "2019-05-10 03:22:57.283628",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Lead",
|
"name": "Lead",
|
||||||
|
|||||||
@@ -102,14 +102,18 @@ class Lead(SellingController):
|
|||||||
|
|
||||||
def has_lost_quotation(self):
|
def has_lost_quotation(self):
|
||||||
return frappe.db.get_value("Quotation", {
|
return frappe.db.get_value("Quotation", {
|
||||||
"lead": self.name,
|
"party_name": self.name,
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"status": "Lost"
|
"status": "Lost"
|
||||||
})
|
})
|
||||||
|
|
||||||
def set_lead_name(self):
|
def set_lead_name(self):
|
||||||
if not self.lead_name:
|
if not self.lead_name:
|
||||||
frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
|
# Check for leads being created through data import
|
||||||
|
if not self.company_name:
|
||||||
|
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
||||||
|
|
||||||
|
self.lead_name = self.company_name
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_customer(source_name, target_doc=None):
|
def make_customer(source_name, target_doc=None):
|
||||||
@@ -163,7 +167,7 @@ def make_quotation(source_name, target_doc=None):
|
|||||||
{"Lead": {
|
{"Lead": {
|
||||||
"doctype": "Quotation",
|
"doctype": "Quotation",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"name": "lead"
|
"name": "party_name"
|
||||||
}
|
}
|
||||||
}}, target_doc)
|
}}, target_doc)
|
||||||
target_doc.quotation_to = "Lead"
|
target_doc.quotation_to = "Lead"
|
||||||
@@ -225,4 +229,4 @@ def make_lead_from_communication(communication, ignore_communication_links=False
|
|||||||
lead_name = lead.name
|
lead_name = lead.name
|
||||||
|
|
||||||
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
|
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
|
||||||
return lead_name
|
return lead_name
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ def get_data():
|
|||||||
'Quotation': 'party_name',
|
'Quotation': 'party_name',
|
||||||
'Opportunity': 'party_name'
|
'Opportunity': 'party_name'
|
||||||
},
|
},
|
||||||
|
'dynamic_links': {
|
||||||
|
'party_name': ['Lead', 'quotation_to']
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'items': ['Opportunity', 'Quotation']
|
'items': ['Opportunity', 'Quotation']
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Customer/Lead",
|
"label": "Party",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "customer",
|
"oldfieldname": "customer",
|
||||||
@@ -1468,7 +1468,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-25 18:55:43.874656",
|
"modified": "2019-05-11 19:22:33.533487",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
|||||||
@@ -17,5 +17,13 @@ frappe.listview_settings['Opportunity'] = {
|
|||||||
listview.page.add_menu_item(__("Set as Closed"), function() {
|
listview.page.add_menu_item(__("Set as Closed"), function() {
|
||||||
listview.call_for_selected_items(method, {"status": "Closed"});
|
listview.call_for_selected_items(method, {"status": "Closed"});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
listview.page.fields_dict.opportunity_from.get_query = function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", ["Customer", "Lead"]],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ def get_healthcare_services_to_invoice(patient):
|
|||||||
'service': service_item, 'rate': practitioner_charge,
|
'service': service_item, 'rate': practitioner_charge,
|
||||||
'income_account': income_account})
|
'income_account': income_account})
|
||||||
|
|
||||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False})
|
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
|
||||||
if lab_tests:
|
if lab_tests:
|
||||||
for lab_test in lab_tests:
|
for lab_test in lab_tests:
|
||||||
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
||||||
|
|||||||
@@ -3,13 +3,8 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Additional Salary', {
|
frappe.ui.form.on('Additional Salary', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("salary_component", function() {
|
frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
is_additional_component: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
frm.set_query("employee", function() {
|
frm.set_query("employee", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
|
"allow_events_in_timeline": 0,
|
||||||
"allow_guest_to_view": 0,
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -54,6 +56,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "employee",
|
"fieldname": "employee",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -87,6 +90,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "salary_component",
|
"fieldname": "salary_component",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -120,6 +124,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -153,6 +158,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "1",
|
"default": "1",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "overwrite_salary_structure_amount",
|
"fieldname": "overwrite_salary_structure_amount",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -185,6 +191,40 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_5",
|
"fieldname": "column_break_5",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -217,6 +257,8 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
|
"description": "Date on which this component is applied",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "payroll_date",
|
"fieldname": "payroll_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -250,6 +292,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fetch_from": "employee.employee_name",
|
"fetch_from": "employee.employee_name",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "employee_name",
|
"fieldname": "employee_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -284,6 +327,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fetch_from": "employee.department",
|
"fetch_from": "employee.department",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "department",
|
"fieldname": "department",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -317,6 +361,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -350,6 +395,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "salary_slip",
|
"fieldname": "salary_slip",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -384,6 +430,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fetch_from": "salary_component.type",
|
"fetch_from": "salary_component.type",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "type",
|
"fieldname": "type",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -417,6 +464,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -453,7 +501,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-08-21 14:44:22.921926",
|
"modified": "2019-05-09 19:53:37.475839",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Additional Salary",
|
"name": "Additional Salary",
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ class AdditionalSalary(Document):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_additional_salary_component(employee, start_date, end_date):
|
def get_additional_salary_component(employee, start_date, end_date):
|
||||||
additional_components = frappe.db.sql("""
|
additional_components = frappe.db.sql("""
|
||||||
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount from `tabAdditional Salary`
|
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
|
||||||
|
from `tabAdditional Salary`
|
||||||
where employee=%(employee)s
|
where employee=%(employee)s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
and payroll_date between %(from_date)s and %(to_date)s
|
and payroll_date between %(from_date)s and %(to_date)s
|
||||||
@@ -48,16 +49,20 @@ def get_additional_salary_component(employee, start_date, end_date):
|
|||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
additional_components_list = []
|
additional_components_list = []
|
||||||
|
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
|
||||||
for d in additional_components:
|
for d in additional_components:
|
||||||
component = frappe.get_doc("Salary Component", d.salary_component)
|
struct_row = frappe._dict({'salary_component': d.salary_component})
|
||||||
struct_row = {'salary_component': d.salary_component}
|
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
|
||||||
for field in ["depends_on_payment_days", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
|
if component:
|
||||||
struct_row[field] = component.get(field)
|
struct_row.update(component[0])
|
||||||
|
|
||||||
additional_components_list.append({
|
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
|
||||||
|
struct_row['is_additional_component'] = 1
|
||||||
|
|
||||||
|
additional_components_list.append(frappe._dict({
|
||||||
'amount': d.amount,
|
'amount': d.amount,
|
||||||
'type': component.type,
|
'type': component[0].type,
|
||||||
'struct_row': struct_row,
|
'struct_row': struct_row,
|
||||||
'overwrite': d.overwrite_salary_structure_amount
|
'overwrite': d.overwrite_salary_structure_amount,
|
||||||
})
|
}))
|
||||||
return additional_components_list
|
return additional_components_list
|
||||||
@@ -3,13 +3,18 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Employee Benefit Application', {
|
frappe.ui.form.on('Employee Benefit Application', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("earning_component", "employee_benefits", function() {
|
if(!frm.doc.employee || !frm.doc.date) {
|
||||||
return {
|
frappe.throw(__("Please select Employee and Date first"));
|
||||||
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
} else {
|
||||||
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
frm.set_query("earning_component", "employee_benefits", function() {
|
||||||
};
|
return {
|
||||||
});
|
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
||||||
|
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
var method, args;
|
var method, args;
|
||||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
|
from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days
|
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
|
||||||
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
||||||
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
|
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
|
|||||||
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
|
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
|
||||||
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
|
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
|
||||||
have_depends_on_payment_days = True
|
have_depends_on_payment_days = True
|
||||||
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount)
|
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
|
||||||
amount_per_day = benefit_amount / payroll_period_days
|
amount_per_day = benefit_amount / payroll_period_days
|
||||||
per_day_amount_total += amount_per_day
|
per_day_amount_total += amount_per_day
|
||||||
|
|
||||||
@@ -167,16 +167,14 @@ def calculate_lwp(employee, start_date, holidays, working_days):
|
|||||||
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
||||||
return lwp
|
return lwp
|
||||||
|
|
||||||
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct, period_length, frequency):
|
def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
|
||||||
payroll_period, period_factor, actual_payroll_days = get_payroll_period_days(start_date, end_date, employee)
|
|
||||||
|
|
||||||
if not payroll_period:
|
if not payroll_period:
|
||||||
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
|
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
|
||||||
.format(struct_row.salary_component))
|
.format(salary_component))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Considering there is only one application for a year
|
# Considering there is only one application for a year
|
||||||
benefit_application_name = frappe.db.sql("""
|
benefit_application = frappe.db.sql("""
|
||||||
select name
|
select name
|
||||||
from `tabEmployee Benefit Application`
|
from `tabEmployee Benefit Application`
|
||||||
where
|
where
|
||||||
@@ -185,69 +183,58 @@ def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal
|
|||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
""", {
|
""", {
|
||||||
'employee': employee,
|
'employee': employee,
|
||||||
'payroll_period': payroll_period
|
'payroll_period': payroll_period.name
|
||||||
})
|
})
|
||||||
|
|
||||||
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_payment_days") != 1:
|
current_benefit_amount = 0.0
|
||||||
if frequency == "Monthly" and actual_payroll_days in range(360, 370):
|
component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
|
||||||
period_length = 1
|
salary_component, ["max_benefit_amount", "depends_on_payment_days"])
|
||||||
period_factor = 12
|
|
||||||
|
|
||||||
if period_factor:
|
if benefit_application:
|
||||||
# If there is application for benefit then fetch the amount from the application.
|
benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
|
||||||
# else Split the max benefits to the pro-rata components with the ratio of their max_benefit_amount
|
{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
|
||||||
if benefit_application_name:
|
elif component_max_benefit:
|
||||||
benefit_application = frappe.get_doc("Employee Benefit Application", benefit_application_name[0][0])
|
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
|
||||||
return get_benefit_amount(benefit_application, struct_row, period_factor, period_length)
|
|
||||||
|
|
||||||
# TODO: Check if there is benefit claim for employee then pro-rata divide the rest of amount (Late Benefit Application)
|
current_benefit_amount = 0
|
||||||
else:
|
if benefit_amount:
|
||||||
component_max = frappe.db.get_value("Salary Component", struct_row.salary_component, "max_benefit_amount")
|
total_sub_periods = get_period_factor(employee,
|
||||||
if component_max:
|
start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
|
||||||
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, component_max)
|
|
||||||
return get_amount(period_factor, benefit_amount, period_length)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_benefit_pro_rata_ratio_amount(sal_struct, component_max):
|
current_benefit_amount = benefit_amount / total_sub_periods
|
||||||
total_pro_rata_max = 0
|
|
||||||
|
return current_benefit_amount
|
||||||
|
|
||||||
|
def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
|
||||||
|
max_benefits_total = 0
|
||||||
benefit_amount = 0
|
benefit_amount = 0
|
||||||
for sal_struct_row in sal_struct.get("earnings"):
|
for d in sal_struct.get("earnings"):
|
||||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component",
|
if d.is_flexible_benefit == 1:
|
||||||
sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
|
||||||
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
|
if not component.pay_against_benefit_claim:
|
||||||
total_pro_rata_max += max_benefit_amount
|
max_benefits_total += component.max_benefit_amount
|
||||||
if total_pro_rata_max > 0:
|
|
||||||
benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max
|
if max_benefits_total > 0:
|
||||||
if benefit_amount > component_max:
|
benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total
|
||||||
benefit_amount = component_max
|
if benefit_amount > component_max_benefit:
|
||||||
|
benefit_amount = component_max_benefit
|
||||||
|
|
||||||
return benefit_amount
|
return benefit_amount
|
||||||
|
|
||||||
def get_benefit_amount(application, struct_row, period_factor, period_length):
|
|
||||||
amount = 0
|
|
||||||
for employee_benefit in application.employee_benefits:
|
|
||||||
if employee_benefit.earning_component == struct_row.salary_component:
|
|
||||||
amount += get_amount(period_factor, employee_benefit.amount, period_length)
|
|
||||||
return amount if amount > 0 else False
|
|
||||||
|
|
||||||
def get_amount(period_factor, amount, period_length):
|
|
||||||
amount_per_day = amount / period_factor
|
|
||||||
total_amount = amount_per_day * period_length
|
|
||||||
return total_amount
|
|
||||||
|
|
||||||
def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
|
def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
|
||||||
if len(filters) < 2:
|
if len(filters) < 2:
|
||||||
return {}
|
return {}
|
||||||
employee = filters['employee']
|
|
||||||
date = filters['date']
|
salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
|
||||||
salary_structure = get_assigned_salary_structure(employee, date)
|
|
||||||
|
|
||||||
if salary_structure:
|
if salary_structure:
|
||||||
query = """select salary_component from `tabSalary Detail` where parent = '{salary_structure}'
|
return frappe.db.sql("""
|
||||||
and is_flexible_benefit = 1
|
select salary_component
|
||||||
order by name"""
|
from `tabSalary Detail`
|
||||||
|
where parent = %s and is_flexible_benefit = 1
|
||||||
return frappe.db.sql(query.format(**{
|
order by name
|
||||||
"salary_structure": salary_structure
|
""", salary_structure)
|
||||||
}))
|
else:
|
||||||
|
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
|
||||||
return {}
|
.format(filters['employee'], filters['date']))
|
||||||
@@ -5,9 +5,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import flt
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
|
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
|
||||||
from erpnext.hr.utils import get_payroll_period, get_previous_claimed_amount
|
from erpnext.hr.utils import get_previous_claimed_amount
|
||||||
|
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period
|
||||||
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
||||||
|
|
||||||
class EmployeeBenefitClaim(Document):
|
class EmployeeBenefitClaim(Document):
|
||||||
@@ -97,31 +99,28 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
|
|||||||
benefit_amount_total += benefit_amount
|
benefit_amount_total += benefit_amount
|
||||||
return benefit_amount_total
|
return benefit_amount_total
|
||||||
|
|
||||||
def get_benefit_claim_amount(employee, start_date, end_date, salary_component):
|
def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
|
||||||
query = """select claimed_amount from `tabEmployee Benefit Claim`
|
query = """
|
||||||
where employee=%(employee)s
|
select sum(claimed_amount)
|
||||||
and docstatus = 1 and pay_against_benefit_claim = 1
|
from `tabEmployee Benefit Claim`
|
||||||
|
where
|
||||||
|
employee=%(employee)s
|
||||||
|
and docstatus = 1
|
||||||
|
and pay_against_benefit_claim = 1
|
||||||
|
and claim_date between %(start_date)s and %(end_date)s
|
||||||
"""
|
"""
|
||||||
if not start_date:
|
|
||||||
query += "and claim_date <= %(end_date)s"
|
|
||||||
else:
|
|
||||||
query += "and (claim_date between %(start_date)s and %(end_date)s)"
|
|
||||||
|
|
||||||
if salary_component:
|
if salary_component:
|
||||||
query += "and earning_component = %(earning_component)s"
|
query += " and earning_component = %(earning_component)s"
|
||||||
|
|
||||||
benefit_claim_details = frappe.db.sql(query, {
|
claimed_amount = flt(frappe.db.sql(query, {
|
||||||
'employee': employee,
|
'employee': employee,
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date,
|
'end_date': end_date,
|
||||||
'earning_component': salary_component
|
'earning_component': salary_component
|
||||||
}, as_dict = True)
|
})[0][0])
|
||||||
if benefit_claim_details:
|
|
||||||
claimed_amount = 0
|
return claimed_amount
|
||||||
for claim_detail in benefit_claim_details:
|
|
||||||
claimed_amount += claim_detail.claimed_amount
|
|
||||||
return claimed_amount
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
|
def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
|
||||||
pro_rata_amount = 0
|
pro_rata_amount = 0
|
||||||
@@ -140,11 +139,11 @@ def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payro
|
|||||||
else:
|
else:
|
||||||
pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
|
pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
|
||||||
|
|
||||||
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date, False)
|
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
|
||||||
|
|
||||||
return claimed_amount + pro_rata_amount
|
return claimed_amount + pro_rata_amount
|
||||||
|
|
||||||
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, current_flexi_amount, payroll_period, sal_struct):
|
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct):
|
||||||
max_benefits = get_max_benefits(employee, payroll_period.end_date)
|
max_benefits = get_max_benefits(employee, payroll_period.end_date)
|
||||||
if not max_benefits:
|
if not max_benefits:
|
||||||
max_benefits = 0
|
max_benefits = 0
|
||||||
|
|||||||
@@ -446,6 +446,72 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "other_incomes_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Other Incomes",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "income_from_other_sources",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Income From Other Sources",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@@ -458,7 +524,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-25 16:38:05.847925",
|
"modified": "2019-05-11 16:13:50.472670",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Tax Exemption Declaration",
|
"name": "Employee Tax Exemption Declaration",
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class EmployeeTaxExemptionDeclaration(Document):
|
|||||||
filters = {
|
filters = {
|
||||||
"employee": self.employee,
|
"employee": self.employee,
|
||||||
"payroll_period": self.payroll_period,
|
"payroll_period": self.payroll_period,
|
||||||
"name": ["!=", self.name]
|
"name": ["!=", self.name],
|
||||||
|
"docstatus": ["!=", 2]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if duplicate:
|
if duplicate:
|
||||||
|
|||||||
@@ -448,6 +448,72 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "other_incomes_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Other Incomes",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "income_from_other_sources",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Income From Other Sources",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -558,7 +624,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-25 17:06:36.569549",
|
"modified": "2019-05-13 12:17:18.045171",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Tax Exemption Proof Submission",
|
"name": "Employee Tax Exemption Proof Submission",
|
||||||
|
|||||||
@@ -539,14 +539,15 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
|
|||||||
|
|
||||||
payroll_entry.email_salary_slip(submitted_ss)
|
payroll_entry.email_salary_slip(submitted_ss)
|
||||||
|
|
||||||
payroll_entry.db_set("salary_slips_submitted", 1)
|
payroll_entry.db_set("salary_slips_submitted", 1)
|
||||||
payroll_entry.notify_update()
|
payroll_entry.notify_update()
|
||||||
|
|
||||||
if not submitted_ss and not not_submitted_ss:
|
if not submitted_ss and not not_submitted_ss:
|
||||||
frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
|
frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
|
||||||
|
|
||||||
if not_submitted_ss:
|
if not_submitted_ss:
|
||||||
frappe.msgprint(_("Could not submit some Salary Slips"))
|
frappe.msgprint(_("Could not submit some Salary Slips"))
|
||||||
|
|
||||||
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
|
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select name from `tabPayroll Entry`
|
select name from `tabPayroll Entry`
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff, getdate, formatdate, cint
|
from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import get_holidays_for_employee
|
from erpnext.hr.utils import get_holidays_for_employee
|
||||||
|
|
||||||
@@ -48,12 +48,13 @@ class PayrollPeriod(Document):
|
|||||||
def get_payroll_period_days(start_date, end_date, employee):
|
def get_payroll_period_days(start_date, end_date, employee):
|
||||||
company = frappe.db.get_value("Employee", employee, "company")
|
company = frappe.db.get_value("Employee", employee, "company")
|
||||||
payroll_period = frappe.db.sql("""
|
payroll_period = frappe.db.sql("""
|
||||||
select name, start_date, end_date from `tabPayroll Period`
|
select name, start_date, end_date
|
||||||
where company=%(company)s
|
from `tabPayroll Period`
|
||||||
and (
|
where
|
||||||
(%(start_date)s between start_date and end_date)
|
company=%(company)s
|
||||||
and (%(end_date)s between start_date and end_date)
|
and %(start_date)s between start_date and end_date
|
||||||
)""", {
|
and %(end_date)s between start_date and end_date
|
||||||
|
""", {
|
||||||
'company': company,
|
'company': company,
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date
|
'end_date': end_date
|
||||||
@@ -67,3 +68,38 @@ def get_payroll_period_days(start_date, end_date, employee):
|
|||||||
working_days -= len(holidays)
|
working_days -= len(holidays)
|
||||||
return payroll_period[0][0], working_days, actual_no_of_days
|
return payroll_period[0][0], working_days, actual_no_of_days
|
||||||
return False, False, False
|
return False, False, False
|
||||||
|
|
||||||
|
def get_payroll_period(from_date, to_date, company):
|
||||||
|
payroll_period = frappe.db.sql("""
|
||||||
|
select name, start_date, end_date
|
||||||
|
from `tabPayroll Period`
|
||||||
|
where start_date<=%s and end_date>= %s and company=%s
|
||||||
|
""", (from_date, to_date, company), as_dict=1)
|
||||||
|
|
||||||
|
return payroll_period[0] if payroll_period else None
|
||||||
|
|
||||||
|
def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
|
||||||
|
# TODO if both deduct checked update the factor to make tax consistent
|
||||||
|
period_start, period_end = payroll_period.start_date, payroll_period.end_date
|
||||||
|
joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
|
||||||
|
|
||||||
|
if getdate(joining_date) > getdate(period_start):
|
||||||
|
period_start = joining_date
|
||||||
|
if relieving_date and getdate(relieving_date) < getdate(period_end):
|
||||||
|
period_end = relieving_date
|
||||||
|
|
||||||
|
total_sub_periods, remaining_sub_periods = 0.0, 0.0
|
||||||
|
|
||||||
|
if payroll_frequency == "Monthly" and not depends_on_payment_days:
|
||||||
|
total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
|
||||||
|
remaining_sub_periods = month_diff(period_end, start_date)
|
||||||
|
else:
|
||||||
|
salary_days = date_diff(end_date, start_date) + 1
|
||||||
|
|
||||||
|
days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1
|
||||||
|
total_sub_periods = flt(days_in_payroll_period) / flt(salary_days)
|
||||||
|
|
||||||
|
remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1
|
||||||
|
remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days)
|
||||||
|
|
||||||
|
return total_sub_periods, remaining_sub_periods
|
||||||
|
|||||||
@@ -24,14 +24,6 @@ frappe.ui.form.on('Salary Component', {
|
|||||||
is_flexible_benefit: function(frm) {
|
is_flexible_benefit: function(frm) {
|
||||||
if(frm.doc.is_flexible_benefit){
|
if(frm.doc.is_flexible_benefit){
|
||||||
set_value_for_condition_and_formula(frm);
|
set_value_for_condition_and_formula(frm);
|
||||||
frm.set_value("is_additional_component", 0);
|
|
||||||
frm.set_value("formula", '');
|
|
||||||
frm.set_value("amount", 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
is_additional_component: function(frm) {
|
|
||||||
if(frm.doc.is_additional_component){
|
|
||||||
frm.set_value("is_flexible_benefit", 0);
|
|
||||||
frm.set_value("formula", '');
|
frm.set_value("formula", '');
|
||||||
frm.set_value("amount", 0);
|
frm.set_value("amount", 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,40 +115,6 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "eval:doc.is_flexible_benefit != 1",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "is_additional_component",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Is Additional Component",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -285,6 +251,39 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -424,7 +423,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.type==\"Earning\" && doc.is_additional_component != 1 && doc.statistical_component!=1",
|
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "flexible_benefits",
|
"fieldname": "flexible_benefits",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -458,7 +457,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.is_additional_component != 1",
|
"depends_on": "",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "is_flexible_benefit",
|
"fieldname": "is_flexible_benefit",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@@ -1035,7 +1034,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-16 19:08:55.323567",
|
"modified": "2019-05-13 12:55:55.424370",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Component",
|
"name": "Salary Component",
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
"salary_component": "Leave Encashment",
|
"salary_component": "Leave Encashment",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"is_payable": 1,
|
"is_payable": 1,
|
||||||
"is_tax_applicable": 1,
|
"is_tax_applicable": 1
|
||||||
"is_additional_component": 1
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -19,7 +19,5 @@ def create_salary_component(component_name, **args):
|
|||||||
"salary_component": component_name,
|
"salary_component": component_name,
|
||||||
"type": args.get("type") or "Earning",
|
"type": args.get("type") or "Earning",
|
||||||
"is_payable": args.get("is_payable") or 1,
|
"is_payable": args.get("is_payable") or 1,
|
||||||
"is_tax_applicable": args.get("is_tax_applicable") or 1,
|
"is_tax_applicable": args.get("is_tax_applicable") or 1
|
||||||
"is_additional_component": args.get("is_additional_component") or 1
|
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
@@ -218,41 +218,6 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fetch_from": "salary_component.is_additional_component",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "is_additional_component",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Is Additional Component",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -323,6 +288,39 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -566,6 +564,41 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "",
|
||||||
|
"fetch_from": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "additional_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Additional Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -713,7 +746,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-04-16 19:09:31.726597",
|
"modified": "2019-05-11 17:33:08.508653",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
{fieldname: 'salary_component', columns: 6},
|
{fieldname: 'salary_component', columns: 6},
|
||||||
{fieldname: 'amount', columns: 4}
|
{fieldname: 'amount', columns: 4}
|
||||||
];
|
];
|
||||||
})
|
});
|
||||||
|
|
||||||
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
|
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
|
||||||
return {
|
return {
|
||||||
@@ -19,31 +19,39 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
employee: frm.doc.employee
|
employee: frm.doc.employee
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
frm.set_query("salary_component", "earnings", function() {
|
frm.set_query("salary_component", "earnings", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
type: "earning"
|
type: "earning"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
frm.set_query("salary_component", "deductions", function() {
|
frm.set_query("salary_component", "deductions", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
type: "deduction"
|
type: "deduction"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
frm.set_query("employee", function() {
|
||||||
|
return{
|
||||||
|
query: "erpnext.controllers.queries.employee_query"
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
start_date: function(frm, dt, dn){
|
start_date: function(frm){
|
||||||
if(frm.doc.start_date){
|
if(frm.doc.start_date){
|
||||||
frm.trigger("set_end_date");
|
frm.trigger("set_end_date");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
end_date: function(frm, dt, dn) {
|
end_date: function(frm) {
|
||||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
frm.events.get_emp_and_leave_details(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_end_date: function(frm){
|
set_end_date: function(frm){
|
||||||
@@ -70,43 +78,54 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
frm.trigger("toggle_reqd_fields")
|
|
||||||
var salary_detail_fields = ["formula", "abbr", "statistical_component", "is_tax_applicable",
|
var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
|
||||||
"is_flexible_benefit", "variable_based_on_taxable_salary", "is_additional_component"]
|
|
||||||
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
|
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
|
||||||
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
||||||
},
|
},
|
||||||
|
|
||||||
salary_slip_based_on_timesheet: function(frm, dt, dn) {
|
salary_slip_based_on_timesheet: function(frm) {
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
frm.events.get_emp_and_leave_details(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
payroll_frequency: function(frm, dt, dn) {
|
payroll_frequency: function(frm) {
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
frm.set_value('end_date', '');
|
frm.set_value('end_date', '');
|
||||||
},
|
},
|
||||||
|
|
||||||
employee: function(frm, dt, dn) {
|
employee: function(frm) {
|
||||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
frm.events.get_emp_and_leave_details(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
leave_without_pay: function(frm){
|
||||||
|
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
|
||||||
|
return frappe.call({
|
||||||
|
method: 'process_salary_based_on_leave',
|
||||||
|
doc: frm.doc,
|
||||||
|
args: {"lwp": frm.doc.leave_without_pay},
|
||||||
|
callback: function(r, rt) {
|
||||||
|
frm.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle_fields: function(frm) {
|
toggle_fields: function(frm) {
|
||||||
frm.toggle_display(['hourly_wages', 'timesheets'],
|
frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
|
||||||
cint(frm.doc.salary_slip_based_on_timesheet)==1);
|
|
||||||
|
|
||||||
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
|
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
|
||||||
frm.doc.payroll_frequency!="");
|
frm.doc.payroll_frequency!="");
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
frappe.ui.form.on('Salary Detail', {
|
|
||||||
earnings_remove: function(frm, dt, dn) {
|
|
||||||
calculate_all(frm.doc, dt, dn);
|
|
||||||
},
|
},
|
||||||
deductions_remove: function(frm, dt, dn) {
|
|
||||||
calculate_all(frm.doc, dt, dn);
|
get_emp_and_leave_details: function(frm) {
|
||||||
|
return frappe.call({
|
||||||
|
method: 'get_emp_and_leave_details',
|
||||||
|
doc: frm.doc,
|
||||||
|
callback: function(r, rt) {
|
||||||
|
frm.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -119,133 +138,29 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get leave details
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
var get_emp_and_leave_details = function(doc, dt, dn) {
|
|
||||||
return frappe.call({
|
|
||||||
method: 'get_emp_and_leave_details',
|
|
||||||
doc: locals[dt][dn],
|
|
||||||
callback: function(r, rt) {
|
|
||||||
cur_frm.refresh();
|
|
||||||
calculate_all(doc, dt, dn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
|
|
||||||
if (doc.employee && doc.start_date && doc.end_date) {
|
|
||||||
return $c_obj(doc, 'get_leave_details', {"lwp": doc.leave_without_pay}, function(r, rt) {
|
|
||||||
var doc = locals[dt][dn];
|
|
||||||
cur_frm.refresh();
|
|
||||||
calculate_all(doc, dt, dn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var calculate_all = function(doc, dt, dn) {
|
|
||||||
calculate_earning_total(doc, dt, dn);
|
|
||||||
calculate_ded_total(doc, dt, dn);
|
|
||||||
calculate_net_pay(doc, dt, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.amount = function(doc,dt,dn){
|
|
||||||
var child = locals[dt][dn];
|
|
||||||
if(!doc.salary_structure){
|
|
||||||
frappe.model.set_value(dt,dn, "default_amount", child.amount)
|
|
||||||
}
|
|
||||||
calculate_all(doc, dt, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.depends_on_payment_days = function(doc,dt,dn){
|
|
||||||
calculate_earning_total(doc, dt, dn, true);
|
|
||||||
calculate_ded_total(doc, dt, dn, true);
|
|
||||||
calculate_net_pay(doc, dt, dn);
|
|
||||||
refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate earning total
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
var calculate_earning_total = function(doc, dt, dn, reset_amount) {
|
|
||||||
|
|
||||||
var tbl = doc.earnings || [];
|
|
||||||
var total_earn = 0;
|
|
||||||
for(var i = 0; i < tbl.length; i++){
|
|
||||||
if(cint(tbl[i].depends_on_payment_days) == 1) {
|
|
||||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
|
|
||||||
cint(doc.total_working_days)*100)/100;
|
|
||||||
} else if(reset_amount && tbl[i].default_amount) {
|
|
||||||
tbl[i].amount = tbl[i].default_amount;
|
|
||||||
}
|
|
||||||
if(!tbl[i].do_not_include_in_total) {
|
|
||||||
total_earn += flt(tbl[i].amount);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doc.gross_pay = total_earn;
|
|
||||||
refresh_many(['earnings', 'amount','gross_pay']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate deduction total
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
var calculate_ded_total = function(doc, dt, dn, reset_amount) {
|
|
||||||
var tbl = doc.deductions || [];
|
|
||||||
var total_ded = 0;
|
|
||||||
for(var i = 0; i < tbl.length; i++){
|
|
||||||
if(cint(tbl[i].depends_on_payment_days) == 1) {
|
|
||||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
|
|
||||||
} else if(reset_amount && tbl[i].default_amount) {
|
|
||||||
tbl[i].amount = tbl[i].default_amount;
|
|
||||||
}
|
|
||||||
if(!tbl[i].do_not_include_in_total) {
|
|
||||||
total_ded += flt(tbl[i].amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doc.total_deduction = total_ded;
|
|
||||||
refresh_many(['deductions', 'total_deduction']);
|
|
||||||
}
|
|
||||||
|
|
||||||
var calculate_net_pay = function(doc, dt, dn) {
|
|
||||||
doc.net_pay = flt(doc.gross_pay) - flt(doc.total_deduction);
|
|
||||||
doc.rounded_total = Math.round(doc.net_pay);
|
|
||||||
refresh_many(['net_pay', 'rounded_total']);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.validate = function(doc, dt, dn) {
|
|
||||||
calculate_all(doc, dt, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
|
|
||||||
return{
|
|
||||||
query: "erpnext.controllers.queries.employee_query"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate total working hours, earnings based on hourly wages and totals
|
// calculate total working hours, earnings based on hourly wages and totals
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
var total_work_hours = function(frm, dt, dn) {
|
var total_work_hours = function(frm, dt, dn) {
|
||||||
frm.set_value('total_working_hours', 0);
|
var total_working_hours = 0.0;
|
||||||
|
|
||||||
$.each(frm.doc["timesheets"] || [], function(i, timesheet) {
|
$.each(frm.doc["timesheets"] || [], function(i, timesheet) {
|
||||||
frm.doc.total_working_hours += timesheet.working_hours;
|
total_working_hours += timesheet.working_hours;
|
||||||
});
|
});
|
||||||
frm.refresh_field('total_working_hours');
|
frm.set_value('total_working_hours', total_working_hours);
|
||||||
|
|
||||||
var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
|
var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
|
||||||
|
|
||||||
frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
|
frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
|
||||||
frm.set_value('gross_pay', 0);
|
var gross_pay = 0.0;
|
||||||
|
|
||||||
$.each(frm.doc["earnings"], function(i, earning) {
|
$.each(frm.doc["earnings"], function(i, earning) {
|
||||||
if (earning.salary_component == r.salary_component) {
|
if (earning.salary_component == r.salary_component) {
|
||||||
earning.amount = wages_amount;
|
earning.amount = wages_amount;
|
||||||
frm.refresh_fields('earnings');
|
frm.refresh_fields('earnings');
|
||||||
}
|
}
|
||||||
frm.doc.gross_pay += earning.amount;
|
gross_pay += earning.amount;
|
||||||
});
|
});
|
||||||
|
frm.set_value('gross_pay', gross_pay);
|
||||||
|
|
||||||
frm.refresh_field('gross_pay');
|
frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction);
|
||||||
calculate_net_pay(frm.doc, dt, dn);
|
frm.doc.rounded_total = Math.round(frm.doc.net_pay);
|
||||||
|
refresh_many(['net_pay', 'rounded_total']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_events_in_timeline": 0,
|
"allow_events_in_timeline": 0,
|
||||||
"allow_guest_to_view": 0,
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
"creation": "2013-01-10 16:34:15",
|
"creation": "2013-01-10 16:34:15",
|
||||||
@@ -1042,73 +1042,6 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_01",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "amended_from",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "amended_from",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"options": "Salary Slip",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -1950,6 +1883,41 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Amended From",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"oldfieldname": "amended_from",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"options": "Salary Slip",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@@ -1963,7 +1931,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-05-08 20:16:21.549386",
|
"modified": "2019-05-13 13:10:14.524119",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -45,12 +45,12 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(ss.total_working_days, no_of_days[0])
|
self.assertEqual(ss.total_working_days, no_of_days[0])
|
||||||
self.assertEqual(ss.payment_days, no_of_days[0])
|
self.assertEqual(ss.payment_days, no_of_days[0])
|
||||||
self.assertEqual(ss.earnings[0].amount, 25000)
|
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||||
self.assertEqual(ss.gross_pay, 40500)
|
self.assertEqual(ss.gross_pay, 78000)
|
||||||
self.assertEqual(ss.net_pay, 29918)
|
self.assertEqual(ss.net_pay, 67418.0)
|
||||||
|
|
||||||
def test_salary_slip_with_holidays_excluded(self):
|
def test_salary_slip_with_holidays_excluded(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
@@ -64,13 +64,13 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
|
self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
|
||||||
self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
|
self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
|
||||||
self.assertEqual(ss.earnings[0].amount, 25000)
|
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||||
self.assertEqual(ss.earnings[0].default_amount, 25000)
|
self.assertEqual(ss.earnings[0].default_amount, 50000)
|
||||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||||
self.assertEqual(ss.gross_pay, 40500)
|
self.assertEqual(ss.gross_pay, 78000)
|
||||||
self.assertEqual(ss.net_pay, 29918)
|
self.assertEqual(ss.net_pay, 67418.0)
|
||||||
|
|
||||||
def test_payment_days(self):
|
def test_payment_days(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
@@ -211,7 +211,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
tax_paid = get_tax_paid_in_period(employee)
|
tax_paid = get_tax_paid_in_period(employee)
|
||||||
|
|
||||||
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
|
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
|
||||||
annual_tax = 29700
|
annual_tax = 113567.79
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, annual_tax)
|
self.assertEqual(tax_paid, annual_tax)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
@@ -250,7 +250,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
# total taxable income 416000, 166000 @ 5% ie. 8300
|
# total taxable income 416000, 166000 @ 5% ie. 8300
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, 8300)
|
self.assertEqual(tax_paid, 88607.79)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||||
raise
|
raise
|
||||||
@@ -265,7 +265,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
|
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
|
||||||
tax_paid = get_tax_paid_in_period(employee)
|
tax_paid = get_tax_paid_in_period(employee)
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tax_paid, 25700)
|
self.assertEqual(tax_paid, 121211.48)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||||
raise
|
raise
|
||||||
@@ -365,7 +365,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
|||||||
"salary_component": 'Basic Salary',
|
"salary_component": 'Basic Salary',
|
||||||
"abbr":'BS',
|
"abbr":'BS',
|
||||||
"condition": 'base > 10000',
|
"condition": 'base > 10000',
|
||||||
"formula": 'base*.5',
|
"formula": 'base',
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"amount_based_on_formula": 1
|
"amount_based_on_formula": 1
|
||||||
},
|
},
|
||||||
@@ -386,7 +386,6 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
|||||||
{
|
{
|
||||||
"salary_component": "Leave Encashment",
|
"salary_component": "Leave Encashment",
|
||||||
"abbr": 'LE',
|
"abbr": 'LE',
|
||||||
"is_additional_component": 1,
|
|
||||||
"type": "Earning"
|
"type": "Earning"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -398,7 +397,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
|||||||
"is_flexible_benefit": 1,
|
"is_flexible_benefit": 1,
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"pay_against_benefit_claim": 1,
|
"pay_against_benefit_claim": 1,
|
||||||
"max_benefit_amount": 100000
|
"max_benefit_amount": 100000,
|
||||||
|
"depends_on_payment_days": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"salary_component": "Medical Allowance",
|
"salary_component": "Medical Allowance",
|
||||||
@@ -409,9 +409,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
|||||||
"max_benefit_amount": 15000
|
"max_benefit_amount": 15000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"salary_component": "Perfomance Bonus",
|
"salary_component": "Performance Bonus",
|
||||||
"abbr": 'B',
|
"abbr": 'B',
|
||||||
"is_additional_component": 1,
|
|
||||||
"type": "Earning"
|
"type": "Earning"
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -442,7 +441,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
|||||||
"abbr":'T',
|
"abbr":'T',
|
||||||
"formula": 'base*.1',
|
"formula": 'base*.1',
|
||||||
"type": "Deduction",
|
"type": "Deduction",
|
||||||
"amount_based_on_formula": 1
|
"amount_based_on_formula": 1,
|
||||||
|
"depends_on_payment_days": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
if not test_tax:
|
if not test_tax:
|
||||||
@@ -512,21 +512,23 @@ def create_tax_slab(payroll_period):
|
|||||||
{
|
{
|
||||||
"from_amount": 250000,
|
"from_amount": 250000,
|
||||||
"to_amount": 500000,
|
"to_amount": 500000,
|
||||||
"percent_deduction": 5
|
"percent_deduction": 5.2,
|
||||||
|
"condition": "annual_taxable_earning > 500000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from_amount": 500000,
|
"from_amount": 500001,
|
||||||
"to_amount": 1000000,
|
"to_amount": 1000000,
|
||||||
"percent_deduction": 20
|
"percent_deduction": 20.8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from_amount": 1000000,
|
"from_amount": 1000001,
|
||||||
"percent_deduction": 30
|
"percent_deduction": 31.2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
payroll_period.taxable_salary_slabs = []
|
payroll_period.taxable_salary_slabs = []
|
||||||
for item in data:
|
for item in data:
|
||||||
payroll_period.append("taxable_salary_slabs", item)
|
payroll_period.append("taxable_salary_slabs", item)
|
||||||
|
payroll_period.standard_tax_exemption_amount = 52500
|
||||||
payroll_period.save()
|
payroll_period.save()
|
||||||
|
|
||||||
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
|
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
|
||||||
@@ -559,7 +561,7 @@ def create_additional_salary(employee, payroll_period, amount):
|
|||||||
"doctype": "Additional Salary",
|
"doctype": "Additional Salary",
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
"company": erpnext.get_default_company(),
|
"company": erpnext.get_default_company(),
|
||||||
"salary_component": "Perfomance Bonus",
|
"salary_component": "Performance Bonus",
|
||||||
"payroll_date": salary_date,
|
"payroll_date": salary_date,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"type": "Earning"
|
"type": "Earning"
|
||||||
|
|||||||
@@ -147,7 +147,8 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
source_name: frm.doc.name,
|
source_name: frm.doc.name,
|
||||||
employee: values.employee,
|
employee: values.employee,
|
||||||
as_print: 1,
|
as_print: 1,
|
||||||
print_format: print_format
|
print_format: print_format,
|
||||||
|
for_preview: 1
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var new_window = window.open();
|
var new_window = window.open();
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ def get_existing_assignments(employees, salary_structure,from_date):
|
|||||||
return salary_structures_assignments
|
return salary_structures_assignments
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None):
|
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
|
||||||
def postprocess(source, target):
|
def postprocess(source, target):
|
||||||
if employee:
|
if employee:
|
||||||
employee_details = frappe.db.get_value("Employee", employee,
|
employee_details = frappe.db.get_value("Employee", employee,
|
||||||
@@ -147,7 +147,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
|
|||||||
target.branch = employee_details.branch
|
target.branch = employee_details.branch
|
||||||
target.designation = employee_details.designation
|
target.designation = employee_details.designation
|
||||||
target.department = employee_details.department
|
target.department = employee_details.department
|
||||||
target.run_method('process_salary_structure')
|
target.run_method('process_salary_structure', for_preview=for_preview)
|
||||||
|
|
||||||
doc = get_mapped_doc("Salary Structure", source_name, {
|
doc = get_mapped_doc("Salary Structure", source_name, {
|
||||||
"Salary Structure": {
|
"Salary Structure": {
|
||||||
|
|||||||
@@ -39,18 +39,19 @@ class TestSalaryStructure(unittest.TestCase):
|
|||||||
holiday_list.save()
|
holiday_list.save()
|
||||||
|
|
||||||
def test_amount_totals(self):
|
def test_amount_totals(self):
|
||||||
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||||
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
|
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
|
||||||
if not sal_slip:
|
if not sal_slip:
|
||||||
sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
|
sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
|
||||||
self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
|
self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
|
||||||
self.assertEqual(sal_slip.get("earnings")[0].amount, 25000)
|
self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
|
||||||
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
|
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
|
||||||
self.assertEqual(sal_slip.get("earnings")[2].amount, 12500)
|
self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
|
||||||
self.assertEqual(sal_slip.get("gross_pay"), 40500)
|
self.assertEqual(sal_slip.get("gross_pay"), 78000)
|
||||||
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
|
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
|
||||||
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
|
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
|
||||||
self.assertEqual(sal_slip.get("total_deduction"), 10000)
|
self.assertEqual(sal_slip.get("total_deduction"), 10000)
|
||||||
self.assertEqual(sal_slip.get("net_pay"), 30500)
|
self.assertEqual(sal_slip.get("net_pay"), 68000)
|
||||||
|
|
||||||
def test_whitespaces_in_formula_conditions_fields(self):
|
def test_whitespaces_in_formula_conditions_fields(self):
|
||||||
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
|
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
|
||||||
|
|||||||
@@ -5,9 +5,19 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import time_diff_in_seconds
|
||||||
from erpnext.hr.doctype.employee.employee import get_employee_emails
|
from erpnext.hr.doctype.employee.employee import get_employee_emails
|
||||||
|
|
||||||
class TrainingEvent(Document):
|
class TrainingEvent(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_employee_emails()
|
||||||
|
self.validate_period()
|
||||||
|
|
||||||
|
def set_employee_emails(self):
|
||||||
self.employee_emails = ', '.join(get_employee_emails([d.employee
|
self.employee_emails = ', '.join(get_employee_emails([d.employee
|
||||||
for d in self.employees]))
|
for d in self.employees]))
|
||||||
|
|
||||||
|
def validate_period(self):
|
||||||
|
if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
|
||||||
|
frappe.throw(_('End time cannot be before start time'))
|
||||||
@@ -35,6 +35,8 @@ def execute(filters=None):
|
|||||||
for d in ded_types:
|
for d in ded_types:
|
||||||
row.append(ss_ded_map.get(ss.name, {}).get(d))
|
row.append(ss_ded_map.get(ss.name, {}).get(d))
|
||||||
|
|
||||||
|
row.append(ss.total_loan_repayment)
|
||||||
|
|
||||||
row += [ss.total_deduction, ss.net_pay]
|
row += [ss.total_deduction, ss.net_pay]
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
@@ -67,7 +69,7 @@ def get_columns(salary_slips):
|
|||||||
|
|
||||||
columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
|
columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
|
||||||
[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
|
[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
|
||||||
[_("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
|
[_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
|
||||||
|
|
||||||
return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
|
return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
|
||||||
|
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ def get_employee_fields_label():
|
|||||||
fields = []
|
fields = []
|
||||||
for df in frappe.get_meta("Employee").get("fields"):
|
for df in frappe.get_meta("Employee").get("fields"):
|
||||||
if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type",
|
if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type",
|
||||||
"holiday_list", "branch", "department", "designation", "grade",
|
"holiday_list", "branch", "department", "designation", "grade",
|
||||||
"notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
|
"notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
|
||||||
fields.append({"value": df.fieldname, "label": df.label})
|
fields.append({"value": df.fieldname, "label": df.label})
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -262,12 +262,6 @@ def get_leave_period(from_date, to_date, company):
|
|||||||
if leave_period:
|
if leave_period:
|
||||||
return leave_period
|
return leave_period
|
||||||
|
|
||||||
def get_payroll_period(from_date, to_date, company):
|
|
||||||
payroll_period = frappe.db.sql("""select name, start_date, end_date from
|
|
||||||
`tabPayroll Period`
|
|
||||||
where start_date<=%s and end_date>= %s and company=%s""", (from_date, to_date, company), as_dict=1)
|
|
||||||
return payroll_period[0] if payroll_period else None
|
|
||||||
|
|
||||||
def allocate_earned_leaves():
|
def allocate_earned_leaves():
|
||||||
'''Allocate earned leaves to Employees'''
|
'''Allocate earned leaves to Employees'''
|
||||||
e_leave_types = frappe.get_all("Leave Type",
|
e_leave_types = frappe.get_all("Leave Type",
|
||||||
|
|||||||
@@ -598,4 +598,5 @@ erpnext.patches.v11_1.set_salary_details_submittable
|
|||||||
erpnext.patches.v11_1.rename_depends_on_lwp
|
erpnext.patches.v11_1.rename_depends_on_lwp
|
||||||
erpnext.patches.v11_1.set_missing_title_for_quotation
|
erpnext.patches.v11_1.set_missing_title_for_quotation
|
||||||
execute:frappe.delete_doc("Report", "Inactive Items")
|
execute:frappe.delete_doc("Report", "Inactive Items")
|
||||||
erpnext.patches.v11_1.delete_scheduling_tool
|
erpnext.patches.v11_1.delete_scheduling_tool
|
||||||
|
erpnext.patches.v11_1.update_bank_transaction_status
|
||||||
@@ -70,7 +70,7 @@ def update_instructors(comp_dict):
|
|||||||
emp_details = frappe.get_all("Employee", fields=["name", "company"])
|
emp_details = frappe.get_all("Employee", fields=["name", "company"])
|
||||||
|
|
||||||
for employee in emp_details:
|
for employee in emp_details:
|
||||||
records = comp_dict[employee.company]
|
records = comp_dict[employee.company] if employee.company else []
|
||||||
|
|
||||||
for department in records:
|
for department in records:
|
||||||
when_then.append('''
|
when_then.append('''
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ def execute():
|
|||||||
return
|
return
|
||||||
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Declaration")
|
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Declaration")
|
||||||
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Proof Submission")
|
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Proof Submission")
|
||||||
|
frappe.reload_doc("hr", "doctype", "Employee Grade")
|
||||||
|
frappe.reload_doc("hr", "doctype", "Leave Policy")
|
||||||
|
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Bank Account")
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Tax Withholding Category")
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Allowed To Transact With")
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Finance Book")
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Loyalty Program")
|
||||||
|
|
||||||
|
frappe.reload_doc("stock", "doctype", "Item Barcode")
|
||||||
|
|
||||||
make_custom_fields()
|
make_custom_fields()
|
||||||
|
|
||||||
frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges")
|
frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges")
|
||||||
|
|||||||
@@ -6,5 +6,10 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
stock_settings = frappe.get_doc('Stock Settings')
|
stock_settings = frappe.get_doc('Stock Settings')
|
||||||
|
if stock_settings.default_warehouse and not frappe.db.exists("Warehouse", stock_settings.default_warehouse):
|
||||||
|
stock_settings.default_warehouse = None
|
||||||
|
if stock_settings.stock_uom and not frappe.db.exists("UOM", stock_settings.stock_uom):
|
||||||
|
stock_settings.stock_uom = None
|
||||||
|
stock_settings.flags.ignore_mandatory = True
|
||||||
stock_settings.action_if_quality_inspection_is_not_submitted = "Stop"
|
stock_settings.action_if_quality_inspection_is_not_submitted = "Stop"
|
||||||
stock_settings.save()
|
stock_settings.save()
|
||||||
|
|||||||
15
erpnext/patches/v11_1/update_bank_transaction_status.py
Normal file
15
erpnext/patches/v11_1/update_bank_transaction_status.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "bank_transaction")
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabBank Transaction`
|
||||||
|
SET status = 'Reconciled'
|
||||||
|
WHERE
|
||||||
|
status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
|
||||||
|
and ifnull(allocated_amount, 0) > 0
|
||||||
|
""")
|
||||||
@@ -7,8 +7,11 @@ def execute():
|
|||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'payroll_period')
|
||||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration')
|
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration')
|
||||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission')
|
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission')
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category')
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail')
|
||||||
|
|
||||||
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
|
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
|
||||||
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
|
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
|
||||||
|
|||||||
@@ -429,6 +429,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
set_warehouse: me.frm.doc.set_warehouse,
|
set_warehouse: me.frm.doc.set_warehouse,
|
||||||
warehouse: item.warehouse,
|
warehouse: item.warehouse,
|
||||||
customer: me.frm.doc.customer || me.frm.doc.party_name,
|
customer: me.frm.doc.customer || me.frm.doc.party_name,
|
||||||
|
quotation_to: me.frm.doc.quotation_to,
|
||||||
supplier: me.frm.doc.supplier,
|
supplier: me.frm.doc.supplier,
|
||||||
currency: me.frm.doc.currency,
|
currency: me.frm.doc.currency,
|
||||||
update_stock: update_stock,
|
update_stock: update_stock,
|
||||||
|
|||||||
@@ -154,11 +154,9 @@ def calculate_annual_eligible_hra_exemption(doc):
|
|||||||
hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
|
hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
|
||||||
if not (basic_component and hra_component):
|
if not (basic_component and hra_component):
|
||||||
frappe.throw(_("Please mention Basic and HRA component in Company"))
|
frappe.throw(_("Please mention Basic and HRA component in Company"))
|
||||||
|
|
||||||
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
|
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
|
||||||
if hra_component and basic_component:
|
if hra_component and basic_component:
|
||||||
assignment = get_salary_assignment(doc.employee, nowdate())
|
assignment = get_salary_assignment(doc.employee, nowdate())
|
||||||
|
|
||||||
if assignment:
|
if assignment:
|
||||||
hra_component_exists = frappe.db.exists("Salary Detail", {
|
hra_component_exists = frappe.db.exists("Salary Detail", {
|
||||||
"parent": assignment.salary_structure,
|
"parent": assignment.salary_structure,
|
||||||
@@ -166,18 +164,19 @@ def calculate_annual_eligible_hra_exemption(doc):
|
|||||||
"parentfield": "earnings",
|
"parentfield": "earnings",
|
||||||
"parenttype": "Salary Structure"
|
"parenttype": "Salary Structure"
|
||||||
})
|
})
|
||||||
|
|
||||||
if hra_component_exists:
|
if hra_component_exists:
|
||||||
basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee,
|
basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee,
|
||||||
assignment.salary_structure, basic_component, hra_component)
|
assignment.salary_structure, basic_component, hra_component)
|
||||||
if hra_amount:
|
if hra_amount:
|
||||||
if doc.monthly_house_rent:
|
if doc.monthly_house_rent:
|
||||||
annual_exemption = calculate_hra_exemption(assignment.salary_structure,
|
annual_exemption = calculate_hra_exemption(assignment.salary_structure,
|
||||||
basic_amount, hra_amount, doc.monthly_house_rent,
|
basic_amount, hra_amount, doc.monthly_house_rent, doc.rented_in_metro_city)
|
||||||
doc.rented_in_metro_city)
|
|
||||||
if annual_exemption > 0:
|
if annual_exemption > 0:
|
||||||
monthly_exemption = annual_exemption / 12
|
monthly_exemption = annual_exemption / 12
|
||||||
else:
|
else:
|
||||||
annual_exemption = 0
|
annual_exemption = 0
|
||||||
|
|
||||||
elif doc.docstatus == 1:
|
elif doc.docstatus == 1:
|
||||||
frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration"))
|
frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration"))
|
||||||
|
|
||||||
@@ -188,7 +187,7 @@ def calculate_annual_eligible_hra_exemption(doc):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
|
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
|
||||||
salary_slip = make_salary_slip(salary_structure, employee=employee)
|
salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1)
|
||||||
basic_amt, hra_amt = 0, 0
|
basic_amt, hra_amt = 0, 0
|
||||||
for earning in salary_slip.earnings:
|
for earning in salary_slip.earnings:
|
||||||
if earning.salary_component == basic_component:
|
if earning.salary_component == basic_component:
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ def get_data():
|
|||||||
'Quotation': 'party_name',
|
'Quotation': 'party_name',
|
||||||
'Opportunity': 'party_name'
|
'Opportunity': 'party_name'
|
||||||
},
|
},
|
||||||
|
'dynamic_links': {
|
||||||
|
'party_name': ['Customer', 'quotation_to']
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Pre Sales'),
|
'label': _('Pre Sales'),
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 1,
|
||||||
"label": "Quotation To",
|
"label": "Quotation To",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 1,
|
||||||
"label": "Party",
|
"label": "Party",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -3224,7 +3224,7 @@
|
|||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"menu_index": 0,
|
"menu_index": 0,
|
||||||
"modified": "2019-05-07 14:29:22.565474",
|
"modified": "2019-05-11 19:26:50.735628",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation",
|
"name": "Quotation",
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
frappe.listview_settings['Quotation'] = {
|
frappe.listview_settings['Quotation'] = {
|
||||||
add_fields: ["customer_name", "base_grand_total", "status",
|
add_fields: ["customer_name", "base_grand_total", "status",
|
||||||
"company", "currency", 'valid_till'],
|
"company", "currency", 'valid_till'],
|
||||||
|
|
||||||
|
onload: function(listview) {
|
||||||
|
listview.page.fields_dict.quotation_to.get_query = function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", ["Customer", "Lead"]],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(doc.status==="Submitted") {
|
if(doc.status==="Submitted") {
|
||||||
if (doc.valid_till && doc.valid_till < frappe.datetime.nowdate()) {
|
if (doc.valid_till && doc.valid_till < frappe.datetime.nowdate()) {
|
||||||
|
|||||||
@@ -123,6 +123,44 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||||
|
|
||||||
|
def test_return_against_sales_order(self):
|
||||||
|
so = make_sales_order()
|
||||||
|
|
||||||
|
dn = create_dn_against_so(so.name, 6)
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 6)
|
||||||
|
|
||||||
|
# Check delivered_qty after make_sales_invoice with update_stock checked
|
||||||
|
si2 = make_sales_invoice(so.name)
|
||||||
|
si2.set("update_stock", 1)
|
||||||
|
si2.get("items")[0].qty = 3
|
||||||
|
si2.insert()
|
||||||
|
si2.submit()
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||||
|
|
||||||
|
# Make return deliver note, sales invoice and check quantity
|
||||||
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
|
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3, do_not_submit=True)
|
||||||
|
dn1.items[0].against_sales_order = so.name
|
||||||
|
dn1.items[0].so_detail = so.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
|
si1 = create_sales_invoice(is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||||
|
si1.items[0].sales_order = so.name
|
||||||
|
si1.items[0].so_detail = so.items[0].name
|
||||||
|
si1.submit()
|
||||||
|
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 5)
|
||||||
|
|
||||||
|
|
||||||
def test_reserved_qty_for_partial_delivery(self):
|
def test_reserved_qty_for_partial_delivery(self):
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||||
existing_reserved_qty = get_reserved_qty()
|
existing_reserved_qty = get_reserved_qty()
|
||||||
|
|||||||
@@ -11,11 +11,6 @@ frappe.ui.form.on("Company", {
|
|||||||
filters: {"type": "Earning"}
|
filters: {"type": "Earning"}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
frm.set_query("arrear_component", function(){
|
|
||||||
return {
|
|
||||||
filters: {"is_additional_component": 1}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("parent_company", function() {
|
frm.set_query("parent_company", function() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -52,16 +52,20 @@ class DeliveryNote(SellingController):
|
|||||||
'percent_join_field': 'against_sales_invoice',
|
'percent_join_field': 'against_sales_invoice',
|
||||||
'overflow_type': 'delivery',
|
'overflow_type': 'delivery',
|
||||||
'no_tolerance': 1
|
'no_tolerance': 1
|
||||||
},
|
|
||||||
{
|
|
||||||
'source_dt': 'Delivery Note Item',
|
|
||||||
'target_dt': 'Sales Order Item',
|
|
||||||
'join_field': 'so_detail',
|
|
||||||
'target_field': 'returned_qty',
|
|
||||||
'target_parent_dt': 'Sales Order',
|
|
||||||
'source_field': '-1 * qty',
|
|
||||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
|
||||||
}]
|
}]
|
||||||
|
if cint(self.is_return):
|
||||||
|
self.status_updater.append({
|
||||||
|
'source_dt': 'Delivery Note Item',
|
||||||
|
'target_dt': 'Sales Order Item',
|
||||||
|
'join_field': 'so_detail',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Sales Order',
|
||||||
|
'source_field': '-1 * qty',
|
||||||
|
'second_source_dt': 'Sales Invoice Item',
|
||||||
|
'second_source_field': '-1 * qty',
|
||||||
|
'second_join_field': 'so_detail',
|
||||||
|
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||||
|
})
|
||||||
|
|
||||||
def before_print(self):
|
def before_print(self):
|
||||||
def toggle_print_hide(meta, fieldname):
|
def toggle_print_hide(meta, fieldname):
|
||||||
|
|||||||
@@ -24,29 +24,32 @@ class PurchaseReceipt(BuyingController):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Purchase Receipt Item',
|
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
'join_field': 'purchase_order_item',
|
'join_field': 'purchase_order_item',
|
||||||
'target_field': 'received_qty',
|
'target_field': 'received_qty',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'target_parent_field': 'per_received',
|
'target_parent_field': 'per_received',
|
||||||
'target_ref_field': 'qty',
|
'target_ref_field': 'qty',
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'source_field': 'received_qty',
|
'source_field': 'received_qty',
|
||||||
|
'second_source_dt': 'Purchase Invoice Item',
|
||||||
|
'second_source_field': 'received_qty',
|
||||||
|
'second_join_field': 'po_detail',
|
||||||
'percent_join_field': 'purchase_order',
|
'percent_join_field': 'purchase_order',
|
||||||
'overflow_type': 'receipt'
|
'overflow_type': 'receipt'
|
||||||
},
|
|
||||||
{
|
|
||||||
'source_dt': 'Purchase Receipt Item',
|
|
||||||
'target_dt': 'Purchase Order Item',
|
|
||||||
'join_field': 'purchase_order_item',
|
|
||||||
'target_field': 'returned_qty',
|
|
||||||
'target_parent_dt': 'Purchase Order',
|
|
||||||
# 'target_parent_field': 'per_received',
|
|
||||||
# 'target_ref_field': 'qty',
|
|
||||||
'source_field': '-1 * qty',
|
|
||||||
# 'overflow_type': 'receipt',
|
|
||||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
|
||||||
}]
|
}]
|
||||||
|
if cint(self.is_return):
|
||||||
|
self.status_updater.append({
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
|
'target_dt': 'Purchase Order Item',
|
||||||
|
'join_field': 'purchase_order_item',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'source_field': '-1 * qty',
|
||||||
|
'second_source_dt': 'Purchase Invoice Item',
|
||||||
|
'second_source_field': '-1 * qty',
|
||||||
|
'second_join_field': 'po_detail',
|
||||||
|
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
||||||
|
})
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint, nowdate
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
from erpnext.stock import get_warehouse_account
|
from erpnext.stock import get_warehouse_account
|
||||||
@@ -140,7 +140,7 @@ class Warehouse(NestedSet):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent=None, company=None, is_root=False):
|
def get_children(doctype, parent=None, company=None, is_root=False):
|
||||||
from erpnext.stock.utils import get_stock_value_from_bin
|
from erpnext.stock.utils import get_stock_value_on
|
||||||
|
|
||||||
if is_root:
|
if is_root:
|
||||||
parent = ""
|
parent = ""
|
||||||
@@ -156,7 +156,7 @@ def get_children(doctype, parent=None, company=None, is_root=False):
|
|||||||
|
|
||||||
# return warehouses
|
# return warehouses
|
||||||
for wh in warehouses:
|
for wh in warehouses:
|
||||||
wh["balance"] = get_stock_value_from_bin(warehouse=wh.value)
|
wh["balance"] = get_stock_value_on(warehouse=wh.value, posting_date=nowdate())
|
||||||
if company:
|
if company:
|
||||||
wh["company_currency"] = frappe.db.get_value('Company', company, 'default_currency')
|
wh["company_currency"] = frappe.db.get_value('Company', company, 'default_currency')
|
||||||
return warehouses
|
return warehouses
|
||||||
|
|||||||
@@ -306,8 +306,21 @@ def get_basic_details(args, item):
|
|||||||
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
|
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
|
||||||
out[fieldname] = item.get(fieldname)
|
out[fieldname] = item.get(fieldname)
|
||||||
|
|
||||||
|
child_doctype = args.doctype + ' Item'
|
||||||
|
meta = frappe.get_meta(child_doctype)
|
||||||
|
if meta.get_field("barcode"):
|
||||||
|
update_barcode_value(out)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def update_barcode_value(out):
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data
|
||||||
|
barcode_data = get_barcode_data([out])
|
||||||
|
|
||||||
|
# If item has one barcode then update the value of the barcode field
|
||||||
|
if barcode_data and len(barcode_data.get(out.item_code)) == 1:
|
||||||
|
out['barcode'] = barcode_data.get(out.item_code)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def calculate_service_end_date(args, item=None):
|
def calculate_service_end_date(args, item=None):
|
||||||
args = process_args(args)
|
args = process_args(args)
|
||||||
@@ -573,6 +586,10 @@ def validate_conversion_rate(args, meta):
|
|||||||
def get_party_item_code(args, item_doc, out):
|
def get_party_item_code(args, item_doc, out):
|
||||||
if args.transaction_type=="selling" and args.customer:
|
if args.transaction_type=="selling" and args.customer:
|
||||||
out.customer_item_code = None
|
out.customer_item_code = None
|
||||||
|
|
||||||
|
if args.quotation_to != 'Customer':
|
||||||
|
return
|
||||||
|
|
||||||
customer_item_code = item_doc.get("customer_items", {"customer_name": args.customer})
|
customer_item_code = item_doc.get("customer_items", {"customer_name": args.customer})
|
||||||
|
|
||||||
if customer_item_code:
|
if customer_item_code:
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["Delayed Order Item Summary"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_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.now_date(),
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"sales_order",
|
||||||
|
label: __("Sales Order"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Sales Order",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"customer",
|
||||||
|
label: __("Customer"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Customer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"customer_group",
|
||||||
|
label: __("Customer Group"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Customer Group",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"item_group",
|
||||||
|
label: __("Item Group"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Item Group",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"based_on",
|
||||||
|
label: __("Based On"),
|
||||||
|
fieldtype: "Select",
|
||||||
|
options: ["Delivery Note", "Sales Invoice"],
|
||||||
|
default: "Sales Invoice",
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"creation": "2019-05-12 13:05:39.162734",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"letter_head": "Gadgets International",
|
||||||
|
"modified": "2019-05-12 13:05:39.162734",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Delayed Order Item Summary",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Delivery Note",
|
||||||
|
"report_name": "Delayed Order Item Summary",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Stock User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Sales Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Maintenance User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Sales User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import date_diff
|
||||||
|
|
||||||
|
def execute(filters=None, consolidated = False):
|
||||||
|
data, columns = DelayedOrderItemSummary(filters).run()
|
||||||
|
|
||||||
|
return data, columns
|
||||||
|
|
||||||
|
class DelayedOrderItemSummary(object):
|
||||||
|
def __init__(self, filters=None):
|
||||||
|
self.filters = frappe._dict(filters or {})
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
return self.get_columns(), self.get_data() or []
|
||||||
|
|
||||||
|
def get_data(self, consolidated=False):
|
||||||
|
conditions = ""
|
||||||
|
|
||||||
|
doctype = self.filters.get("based_on")
|
||||||
|
child_doc= "%s Item" % doctype
|
||||||
|
|
||||||
|
if doctype == "Sales Invoice":
|
||||||
|
conditions = " and `tabSales Invoice`.update_stock = 1 and `tabSales Invoice`.is_pos = 0"
|
||||||
|
|
||||||
|
if self.filters.get("item_group"):
|
||||||
|
conditions += " and `tab%s`.item_group = '%s'" % (child_doc,
|
||||||
|
frappe.db.escape(self.filters.get("item_group")))
|
||||||
|
|
||||||
|
for field in ["customer", "customer_group", "company"]:
|
||||||
|
if self.filters.get(field):
|
||||||
|
conditions += " and `tab%s`.%s = '%s'" % (doctype,
|
||||||
|
field, frappe.db.escape(self.filters.get(field)))
|
||||||
|
|
||||||
|
sales_order_field = "against_sales_order"
|
||||||
|
if doctype == "Sales Invoice":
|
||||||
|
sales_order_field = "sales_order"
|
||||||
|
|
||||||
|
if self.filters.get("sales_order"):
|
||||||
|
conditions = " and `tab%s`.%s = '%s'" %(child_doc, sales_order_field, self.filters.get("sales_order"))
|
||||||
|
|
||||||
|
self.transactions = frappe.db.sql(""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name,
|
||||||
|
`tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount,
|
||||||
|
`tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order,
|
||||||
|
`tab{doctype}`.customer, `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total
|
||||||
|
FROM `tab{child_doc}`, `tab{doctype}`
|
||||||
|
WHERE
|
||||||
|
`tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and
|
||||||
|
`tab{doctype}`.posting_date between %(from_date)s and %(to_date)s and
|
||||||
|
`tab{child_doc}`.{so_field} is not null and `tab{child_doc}`.{so_field} != '' {cond}
|
||||||
|
""".format(cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field), {
|
||||||
|
'from_date': self.filters.get('from_date'),
|
||||||
|
'to_date': self.filters.get('to_date')
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
|
if self.transactions:
|
||||||
|
self.filter_transactions_data(consolidated)
|
||||||
|
|
||||||
|
return self.transactions
|
||||||
|
|
||||||
|
def filter_transactions_data(self, consolidated=False):
|
||||||
|
sales_orders = [d.sales_order for d in self.transactions]
|
||||||
|
doctype = "Sales Order"
|
||||||
|
filters = {'name': ('in', sales_orders)}
|
||||||
|
|
||||||
|
if not consolidated:
|
||||||
|
sales_order_items = [d.so_detail for d in self.transactions]
|
||||||
|
doctype = "Sales Order Item"
|
||||||
|
filters = {'parent': ('in', sales_orders), 'name': ('in', sales_order_items)}
|
||||||
|
|
||||||
|
so_data = {}
|
||||||
|
for d in frappe.get_all(doctype, filters = filters,
|
||||||
|
fields = ["delivery_date", "parent", "name"]):
|
||||||
|
key = d.name if consolidated else (d.parent, d.name)
|
||||||
|
if key not in so_data:
|
||||||
|
so_data.setdefault(key, d.delivery_date)
|
||||||
|
|
||||||
|
for row in self.transactions:
|
||||||
|
key = row.sales_order if consolidated else (row.sales_order, row.so_detail)
|
||||||
|
row.update({
|
||||||
|
'delivery_date': so_data.get(key),
|
||||||
|
'delay_days': date_diff(row.posting_date, so_data.get(key))
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.transactions
|
||||||
|
|
||||||
|
def get_columns(self):
|
||||||
|
based_on = self.filters.get("based_on")
|
||||||
|
|
||||||
|
return [{
|
||||||
|
"label": _(based_on),
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": based_on,
|
||||||
|
"width": 100
|
||||||
|
},{
|
||||||
|
"label": _("Customer"),
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Expected Delivery Date"),
|
||||||
|
"fieldname": "delivery_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Actual Delivery Date"),
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Quantity"),
|
||||||
|
"fieldname": "qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Rate"),
|
||||||
|
"fieldname": "rate",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Delay Days"),
|
||||||
|
"fieldname": "delay_days",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Sales Order"),
|
||||||
|
"fieldname": "sales_order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Sales Order",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer PO"),
|
||||||
|
"fieldname": "po_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 100
|
||||||
|
}]
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["Delayed Order Summary"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_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.now_date(),
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"sales_order",
|
||||||
|
label: __("Sales Order"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Sales Order",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"customer",
|
||||||
|
label: __("Customer"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Customer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"customer_group",
|
||||||
|
label: __("Customer Group"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Customer Group",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"item_group",
|
||||||
|
label: __("Item Group"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Item Group",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"based_on",
|
||||||
|
label: __("Based On"),
|
||||||
|
fieldtype: "Select",
|
||||||
|
options: ["Delivery Note", "Sales Invoice"],
|
||||||
|
default: "Sales Invoice",
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"creation": "2019-05-12 12:38:44.907187",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"letter_head": "Gadgets International",
|
||||||
|
"modified": "2019-05-12 12:39:16.378341",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Delayed Order Summary",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Delivery Note",
|
||||||
|
"report_name": "Delayed Order Summary",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Stock User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Sales Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Maintenance User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Sales User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
from erpnext.stock.report.delayed_order_item_summary.delayed_order_item_summary import DelayedOrderItemSummary
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns, data = [], []
|
||||||
|
|
||||||
|
columns, data = DelayedOrderSummary(filters).run()
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
class DelayedOrderSummary(DelayedOrderItemSummary):
|
||||||
|
def run(self):
|
||||||
|
return self.get_columns(), self.get_data(consolidated=True) or []
|
||||||
|
|
||||||
|
def get_data(self, consolidated=False):
|
||||||
|
data = super(DelayedOrderSummary, self).get_data(consolidated) or []
|
||||||
|
|
||||||
|
so_list = []
|
||||||
|
result = []
|
||||||
|
for d in data:
|
||||||
|
if d.sales_order not in so_list:
|
||||||
|
so_list.append(d.sales_order)
|
||||||
|
result.append(d)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_columns(self):
|
||||||
|
based_on = self.filters.get("based_on")
|
||||||
|
|
||||||
|
return [{
|
||||||
|
"label": _(based_on),
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": based_on,
|
||||||
|
"width": 100
|
||||||
|
},{
|
||||||
|
"label": _("Customer"),
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Expected Delivery Date"),
|
||||||
|
"fieldname": "delivery_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Actual Delivery Date"),
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "grand_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Delay Days"),
|
||||||
|
"fieldname": "delay_days",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Sales Order"),
|
||||||
|
"fieldname": "sales_order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Sales Order",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer PO"),
|
||||||
|
"fieldname": "po_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 100
|
||||||
|
}]
|
||||||
Reference in New Issue
Block a user