Merge branch 'develop' into dev-quality-inspection-accounts
This commit is contained in:
commit
4e25aa77dd
@ -42,10 +42,9 @@ let add_fields_to_mapping_table = function (frm) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.meta.get_docfield("Bank Transaction Mapping", "bank_transaction_field",
|
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
|
||||||
frm.doc.name).options = options;
|
'bank_transaction_field', 'options', options
|
||||||
|
);
|
||||||
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||||
|
@ -293,6 +293,11 @@ def validate_accounts(file_name):
|
|||||||
accounts_dict = {}
|
accounts_dict = {}
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
accounts_dict.setdefault(account["account_name"], account)
|
accounts_dict.setdefault(account["account_name"], account)
|
||||||
|
if not hasattr(account, "parent_account"):
|
||||||
|
msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
|
||||||
|
msg += "<br><br>"
|
||||||
|
msg += _("Alternatively, you can download the template and fill your data in.")
|
||||||
|
frappe.throw(msg, title=_("Parent Account Missing"))
|
||||||
if account["parent_account"] and accounts_dict.get(account["parent_account"]):
|
if account["parent_account"] and accounts_dict.get(account["parent_account"]):
|
||||||
accounts_dict[account["parent_account"]]["is_group"] = 1
|
accounts_dict[account["parent_account"]]["is_group"] = 1
|
||||||
|
|
||||||
|
@ -327,18 +327,16 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setup_balance_formatter: function() {
|
setup_balance_formatter: function() {
|
||||||
var me = this;
|
const formatter = function(value, df, options, doc) {
|
||||||
$.each(["balance", "party_balance"], function(i, field) {
|
var currency = frappe.meta.get_field_currency(df, doc);
|
||||||
var df = frappe.meta.get_docfield("Journal Entry Account", field, me.frm.doc.name);
|
var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
|
||||||
df.formatter = function(value, df, options, doc) {
|
return "<div style='text-align: right'>"
|
||||||
var currency = frappe.meta.get_field_currency(df, doc);
|
+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
|
||||||
var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
|
+ " " + dr_or_cr
|
||||||
return "<div style='text-align: right'>"
|
+ "</div>";
|
||||||
+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
|
};
|
||||||
+ " " + dr_or_cr
|
this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
|
||||||
+ "</div>";
|
this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
reference_name: function(doc, cdt, cdn) {
|
reference_name: function(doc, cdt, cdn) {
|
||||||
@ -431,15 +429,6 @@ cur_frm.cscript.validate = function(doc,cdt,cdn) {
|
|||||||
cur_frm.cscript.update_totals(doc);
|
cur_frm.cscript.update_totals(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
|
|
||||||
if(doc.select_print_heading){
|
|
||||||
// print heading
|
|
||||||
cur_frm.pformat.print_heading = doc.select_print_heading;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cur_frm.pformat.print_heading = __("Journal Entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.ui.form.on("Journal Entry Account", {
|
frappe.ui.form.on("Journal Entry Account", {
|
||||||
party: function(frm, cdt, cdn) {
|
party: function(frm, cdt, cdn) {
|
||||||
var d = frappe.get_doc(cdt, cdn);
|
var d = frappe.get_doc(cdt, cdn);
|
||||||
@ -511,8 +500,11 @@ $.extend(erpnext.journal_entry, {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$.each(field_label_map, function (fieldname, label) {
|
$.each(field_label_map, function (fieldname, label) {
|
||||||
var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
|
frm.fields_dict.accounts.grid.update_docfield_property(
|
||||||
df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
|
fieldname,
|
||||||
|
'label',
|
||||||
|
frm.doc.multi_currency ? (label + " in Account Currency") : label
|
||||||
|
);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-24 14:06:54.833738",
|
"modified": "2020-06-26 14:06:54.833738",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry Account",
|
"name": "Journal Entry Account",
|
||||||
|
@ -234,8 +234,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (invoices) {
|
if (invoices) {
|
||||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
this.frm.fields_dict.payment.grid.update_docfield_property(
|
||||||
me.frm.doc.name).options = "\n" + invoices.join("\n");
|
'invoice_number', 'options', "\n" + invoices.join("\n")
|
||||||
|
);
|
||||||
|
|
||||||
$.each(me.frm.doc.payments || [], function(i, p) {
|
$.each(me.frm.doc.payments || [], function(i, p) {
|
||||||
if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
|
if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
|
||||||
|
@ -108,7 +108,6 @@ class POSInvoice(SalesInvoice):
|
|||||||
filters = { "item_code": d.item_code, "warehouse": d.warehouse }
|
filters = { "item_code": d.item_code, "warehouse": d.warehouse }
|
||||||
if d.batch_no:
|
if d.batch_no:
|
||||||
filters["batch_no"] = d.batch_no
|
filters["batch_no"] = d.batch_no
|
||||||
|
|
||||||
reserved_serial_nos = get_pos_reserved_serial_nos(filters)
|
reserved_serial_nos = get_pos_reserved_serial_nos(filters)
|
||||||
serial_nos = get_serial_nos(d.serial_no)
|
serial_nos = get_serial_nos(d.serial_no)
|
||||||
invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos]
|
invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos]
|
||||||
|
@ -12,6 +12,7 @@ from frappe.utils.background_jobs import enqueue
|
|||||||
from frappe.model.mapper import map_doc, map_child_doc
|
from frappe.model.mapper import map_doc, map_child_doc
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||||
|
import json
|
||||||
|
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
@ -78,8 +79,11 @@ class POSInvoiceMergeLog(Document):
|
|||||||
sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
|
sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
|
||||||
|
|
||||||
sales_invoice.is_consolidated = 1
|
sales_invoice.is_consolidated = 1
|
||||||
|
sales_invoice.set_posting_time = 1
|
||||||
|
sales_invoice.posting_date = getdate(self.posting_date)
|
||||||
sales_invoice.save()
|
sales_invoice.save()
|
||||||
sales_invoice.submit()
|
sales_invoice.submit()
|
||||||
|
|
||||||
self.consolidated_invoice = sales_invoice.name
|
self.consolidated_invoice = sales_invoice.name
|
||||||
|
|
||||||
return sales_invoice.name
|
return sales_invoice.name
|
||||||
@ -91,10 +95,13 @@ class POSInvoiceMergeLog(Document):
|
|||||||
credit_note = self.merge_pos_invoice_into(credit_note, data)
|
credit_note = self.merge_pos_invoice_into(credit_note, data)
|
||||||
|
|
||||||
credit_note.is_consolidated = 1
|
credit_note.is_consolidated = 1
|
||||||
|
credit_note.set_posting_time = 1
|
||||||
|
credit_note.posting_date = getdate(self.posting_date)
|
||||||
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
||||||
# credit_note.return_against = self.consolidated_invoice
|
# credit_note.return_against = self.consolidated_invoice
|
||||||
credit_note.save()
|
credit_note.save()
|
||||||
credit_note.submit()
|
credit_note.submit()
|
||||||
|
|
||||||
self.consolidated_credit_note = credit_note.name
|
self.consolidated_credit_note = credit_note.name
|
||||||
|
|
||||||
return credit_note.name
|
return credit_note.name
|
||||||
@ -131,12 +138,14 @@ class POSInvoiceMergeLog(Document):
|
|||||||
if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
|
if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
|
||||||
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
|
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
|
||||||
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount)
|
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount)
|
||||||
|
update_item_wise_tax_detail(t, tax)
|
||||||
found = True
|
found = True
|
||||||
if not found:
|
if not found:
|
||||||
tax.charge_type = 'Actual'
|
tax.charge_type = 'Actual'
|
||||||
tax.included_in_print_rate = 0
|
tax.included_in_print_rate = 0
|
||||||
tax.tax_amount = tax.tax_amount_after_discount_amount
|
tax.tax_amount = tax.tax_amount_after_discount_amount
|
||||||
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
|
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
|
||||||
|
tax.item_wise_tax_detail = tax.item_wise_tax_detail
|
||||||
taxes.append(tax)
|
taxes.append(tax)
|
||||||
|
|
||||||
for payment in doc.get('payments'):
|
for payment in doc.get('payments'):
|
||||||
@ -168,11 +177,9 @@ class POSInvoiceMergeLog(Document):
|
|||||||
sales_invoice = frappe.new_doc('Sales Invoice')
|
sales_invoice = frappe.new_doc('Sales Invoice')
|
||||||
sales_invoice.customer = self.customer
|
sales_invoice.customer = self.customer
|
||||||
sales_invoice.is_pos = 1
|
sales_invoice.is_pos = 1
|
||||||
# date can be pos closing date?
|
|
||||||
sales_invoice.posting_date = getdate(nowdate())
|
|
||||||
|
|
||||||
return sales_invoice
|
return sales_invoice
|
||||||
|
|
||||||
def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
|
def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
|
||||||
for doc in invoice_docs:
|
for doc in invoice_docs:
|
||||||
doc.load_from_db()
|
doc.load_from_db()
|
||||||
@ -187,6 +194,26 @@ class POSInvoiceMergeLog(Document):
|
|||||||
si.flags.ignore_validate = True
|
si.flags.ignore_validate = True
|
||||||
si.cancel()
|
si.cancel()
|
||||||
|
|
||||||
|
def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
|
||||||
|
consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail)
|
||||||
|
tax_row_detail = json.loads(tax_row.item_wise_tax_detail)
|
||||||
|
|
||||||
|
if not consolidated_tax_detail:
|
||||||
|
consolidated_tax_detail = {}
|
||||||
|
|
||||||
|
for item_code, tax_data in tax_row_detail.items():
|
||||||
|
if consolidated_tax_detail.get(item_code):
|
||||||
|
consolidated_tax_data = consolidated_tax_detail.get(item_code)
|
||||||
|
consolidated_tax_detail.update({
|
||||||
|
item_code: [consolidated_tax_data[0], consolidated_tax_data[1] + tax_data[1]]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
consolidated_tax_detail.update({
|
||||||
|
item_code: [tax_data[0], tax_data[1]]
|
||||||
|
})
|
||||||
|
|
||||||
|
consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail, separators=(',', ':'))
|
||||||
|
|
||||||
def get_all_unconsolidated_invoices():
|
def get_all_unconsolidated_invoices():
|
||||||
filters = {
|
filters = {
|
||||||
'consolidated_invoice': [ 'in', [ '', None ]],
|
'consolidated_invoice': [ 'in', [ '', None ]],
|
||||||
@ -214,7 +241,7 @@ def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
|
|||||||
|
|
||||||
if len(invoices) >= 5 and closing_entry:
|
if len(invoices) >= 5 and closing_entry:
|
||||||
closing_entry.set_status(update=True, status='Queued')
|
closing_entry.set_status(update=True, status='Queued')
|
||||||
enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
|
enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
|
||||||
else:
|
else:
|
||||||
create_merge_logs(invoice_by_customer, closing_entry)
|
create_merge_logs(invoice_by_customer, closing_entry)
|
||||||
|
|
||||||
@ -227,21 +254,21 @@ def unconsolidate_pos_invoices(closing_entry):
|
|||||||
|
|
||||||
if len(merge_logs) >= 5:
|
if len(merge_logs) >= 5:
|
||||||
closing_entry.set_status(update=True, status='Queued')
|
closing_entry.set_status(update=True, status='Queued')
|
||||||
enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
|
enqueue_job(cancel_merge_logs, merge_logs=merge_logs, closing_entry=closing_entry)
|
||||||
else:
|
else:
|
||||||
cancel_merge_logs(merge_logs, closing_entry)
|
cancel_merge_logs(merge_logs, closing_entry)
|
||||||
|
|
||||||
def create_merge_logs(invoice_by_customer, closing_entry={}):
|
def create_merge_logs(invoice_by_customer, closing_entry={}):
|
||||||
for customer, invoices in iteritems(invoice_by_customer):
|
for customer, invoices in iteritems(invoice_by_customer):
|
||||||
merge_log = frappe.new_doc('POS Invoice Merge Log')
|
merge_log = frappe.new_doc('POS Invoice Merge Log')
|
||||||
merge_log.posting_date = getdate(nowdate())
|
merge_log.posting_date = getdate(closing_entry.get('posting_date'))
|
||||||
merge_log.customer = customer
|
merge_log.customer = customer
|
||||||
merge_log.pos_closing_entry = closing_entry.get('name', None)
|
merge_log.pos_closing_entry = closing_entry.get('name', None)
|
||||||
|
|
||||||
merge_log.set('pos_invoices', invoices)
|
merge_log.set('pos_invoices', invoices)
|
||||||
merge_log.save(ignore_permissions=True)
|
merge_log.save(ignore_permissions=True)
|
||||||
merge_log.submit()
|
merge_log.submit()
|
||||||
|
|
||||||
if closing_entry:
|
if closing_entry:
|
||||||
closing_entry.set_status(update=True, status='Submitted')
|
closing_entry.set_status(update=True, status='Submitted')
|
||||||
closing_entry.update_opening_entry()
|
closing_entry.update_opening_entry()
|
||||||
@ -256,7 +283,7 @@ def cancel_merge_logs(merge_logs, closing_entry={}):
|
|||||||
closing_entry.set_status(update=True, status='Cancelled')
|
closing_entry.set_status(update=True, status='Cancelled')
|
||||||
closing_entry.update_opening_entry(for_cancel=True)
|
closing_entry.update_opening_entry(for_cancel=True)
|
||||||
|
|
||||||
def enqueue_job(job, invoice_by_customer, closing_entry):
|
def enqueue_job(job, merge_logs=None, invoice_by_customer=None, closing_entry=None):
|
||||||
check_scheduler_status()
|
check_scheduler_status()
|
||||||
|
|
||||||
job_name = closing_entry.get("name")
|
job_name = closing_entry.get("name")
|
||||||
@ -269,6 +296,7 @@ def enqueue_job(job, invoice_by_customer, closing_entry):
|
|||||||
job_name=job_name,
|
job_name=job_name,
|
||||||
closing_entry=closing_entry,
|
closing_entry=closing_entry,
|
||||||
invoice_by_customer=invoice_by_customer,
|
invoice_by_customer=invoice_by_customer,
|
||||||
|
merge_logs=merge_logs,
|
||||||
now=frappe.conf.developer_mode or frappe.flags.in_test
|
now=frappe.conf.developer_mode or frappe.flags.in_test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import json
|
||||||
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
|
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
|
||||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
||||||
@ -99,4 +100,51 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
|||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
def test_consolidated_invoice_item_taxes(self):
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
try:
|
||||||
|
inv = create_pos_invoice(qty=1, rate=100, do_not_save=True)
|
||||||
|
|
||||||
|
inv.append("taxes", {
|
||||||
|
"account_head": "_Test Account VAT - _TC",
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "VAT",
|
||||||
|
"doctype": "Sales Taxes and Charges",
|
||||||
|
"rate": 9
|
||||||
|
})
|
||||||
|
inv.insert()
|
||||||
|
inv.submit()
|
||||||
|
|
||||||
|
inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
|
||||||
|
inv2.get('items')[0].item_code = '_Test Item 2'
|
||||||
|
inv2.append("taxes", {
|
||||||
|
"account_head": "_Test Account VAT - _TC",
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "VAT",
|
||||||
|
"doctype": "Sales Taxes and Charges",
|
||||||
|
"rate": 5
|
||||||
|
})
|
||||||
|
inv2.insert()
|
||||||
|
inv2.submit()
|
||||||
|
|
||||||
|
consolidate_pos_invoices()
|
||||||
|
inv.load_from_db()
|
||||||
|
|
||||||
|
consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
|
||||||
|
item_wise_tax_detail = json.loads(consolidated_invoice.get('taxes')[0].item_wise_tax_detail)
|
||||||
|
|
||||||
|
tax_rate, amount = item_wise_tax_detail.get('_Test Item')
|
||||||
|
self.assertEqual(tax_rate, 9)
|
||||||
|
self.assertEqual(amount, 9)
|
||||||
|
|
||||||
|
tax_rate2, amount2 = item_wise_tax_detail.get('_Test Item 2')
|
||||||
|
self.assertEqual(tax_rate2, 5)
|
||||||
|
self.assertEqual(amount2, 5)
|
||||||
|
finally:
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
@ -62,14 +62,15 @@ class POSProfile(Document):
|
|||||||
|
|
||||||
if len(default_mode) > 1:
|
if len(default_mode) > 1:
|
||||||
frappe.throw(_("You can only select one mode of payment as default"))
|
frappe.throw(_("You can only select one mode of payment as default"))
|
||||||
|
|
||||||
invalid_modes = []
|
invalid_modes = []
|
||||||
for d in self.payments:
|
for d in self.payments:
|
||||||
account = frappe.db.get_value(
|
account = frappe.db.get_value(
|
||||||
"Mode of Payment Account",
|
"Mode of Payment Account",
|
||||||
{"parent": d.mode_of_payment, "company": self.company},
|
{"parent": d.mode_of_payment, "company": self.company},
|
||||||
"default_account"
|
"default_account"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
||||||
|
|
||||||
|
@ -92,11 +92,21 @@ def make_pos_profile(**args):
|
|||||||
"write_off_cost_center": args.write_off_cost_center or "_Test Write Off Cost Center - _TC"
|
"write_off_cost_center": args.write_off_cost_center or "_Test Write Off Cost Center - _TC"
|
||||||
})
|
})
|
||||||
|
|
||||||
payments = [{
|
mode_of_payment = frappe.get_doc("Mode of Payment", "Cash")
|
||||||
|
company = args.company or "_Test Company"
|
||||||
|
default_account = args.income_account or "Sales - _TC"
|
||||||
|
|
||||||
|
if not frappe.db.get_value("Mode of Payment Account", {"company": company, "parent": "Cash"}):
|
||||||
|
mode_of_payment.append("accounts", {
|
||||||
|
"company": company,
|
||||||
|
"default_account": default_account
|
||||||
|
})
|
||||||
|
mode_of_payment.save()
|
||||||
|
|
||||||
|
pos_profile.append("payments", {
|
||||||
'mode_of_payment': 'Cash',
|
'mode_of_payment': 'Cash',
|
||||||
'default': 1
|
'default': 1
|
||||||
}]
|
})
|
||||||
pos_profile.set("payments", payments)
|
|
||||||
|
|
||||||
if not frappe.db.exists("POS Profile", args.name or "_Test POS Profile"):
|
if not frappe.db.exists("POS Profile", args.name or "_Test POS Profile"):
|
||||||
pos_profile.insert()
|
pos_profile.insert()
|
||||||
|
@ -16,8 +16,11 @@ frappe.ui.form.on('POS Settings', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""].concat(fields);
|
frm.fields_dict.invoice_fields.grid.update_docfield_property(
|
||||||
|
'fieldname', 'options', [""].concat(fields)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -328,6 +328,21 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
self.assertEquals(item.discount_amount, 110)
|
self.assertEquals(item.discount_amount, 110)
|
||||||
self.assertEquals(item.rate, 990)
|
self.assertEquals(item.rate, 990)
|
||||||
|
|
||||||
|
def test_pricing_rule_with_margin_and_discount_amount(self):
|
||||||
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
|
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10,
|
||||||
|
rate_or_discount="Discount Amount", discount_amount=110)
|
||||||
|
si = create_sales_invoice(do_not_save=True)
|
||||||
|
si.items[0].price_list_rate = 1000
|
||||||
|
si.payment_schedule = []
|
||||||
|
si.insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
item = si.items[0]
|
||||||
|
self.assertEquals(item.margin_rate_or_amount, 10)
|
||||||
|
self.assertEquals(item.rate_with_margin, 1100)
|
||||||
|
self.assertEquals(item.discount_amount, 110)
|
||||||
|
self.assertEquals(item.rate, 990)
|
||||||
|
|
||||||
def test_pricing_rule_for_product_discount_on_same_item(self):
|
def test_pricing_rule_for_product_discount_on_same_item(self):
|
||||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
test_record = {
|
test_record = {
|
||||||
@ -560,6 +575,7 @@ def make_pricing_rule(**args):
|
|||||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||||
"condition": args.condition or '',
|
"condition": args.condition or '',
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
|
"discount_amount": args.discount_amount or 0.0,
|
||||||
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -471,7 +471,7 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
|
|
||||||
if not d.get(pr_field): continue
|
if not d.get(pr_field): continue
|
||||||
|
|
||||||
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
if d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field):
|
||||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||||
.format(doc.name))
|
.format(doc.name))
|
||||||
else:
|
else:
|
||||||
|
@ -92,7 +92,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
|||||||
frm.refresh_field('customers');
|
frm.refresh_field('customers');
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
frappe.msgprint('No Customers found with selected options.');
|
frappe.throw('No Customers found with selected options.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,9 +126,11 @@ def get_customers_based_on_sales_person(sales_person):
|
|||||||
sales_person_records = frappe._dict()
|
sales_person_records = frappe._dict()
|
||||||
for d in records:
|
for d in records:
|
||||||
sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||||
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
if sales_person_records.get('Customer'):
|
||||||
|
return frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||||
filters=[['name', 'in', list(sales_person_records['Customer'])]])
|
filters=[['name', 'in', list(sales_person_records['Customer'])]])
|
||||||
return customers
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
def get_recipients_and_cc(customer, doc):
|
def get_recipients_and_cc(customer, doc):
|
||||||
recipients = []
|
recipients = []
|
||||||
|
@ -496,15 +496,6 @@ cur_frm.fields_dict['items'].grid.get_field('project').get_query = function(doc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
|
|
||||||
if(doc.select_print_heading){
|
|
||||||
// print heading
|
|
||||||
cur_frm.pformat.print_heading = doc.select_print_heading;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cur_frm.pformat.print_heading = __("Purchase Invoice");
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.ui.form.on("Purchase Invoice", {
|
frappe.ui.form.on("Purchase Invoice", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.custom_make_buttons = {
|
frm.custom_make_buttons = {
|
||||||
|
@ -127,7 +127,6 @@
|
|||||||
"write_off_cost_center",
|
"write_off_cost_center",
|
||||||
"advances_section",
|
"advances_section",
|
||||||
"allocate_advances_automatically",
|
"allocate_advances_automatically",
|
||||||
"adjust_advance_taxes",
|
|
||||||
"get_advances",
|
"get_advances",
|
||||||
"advances",
|
"advances",
|
||||||
"payment_schedule_section",
|
"payment_schedule_section",
|
||||||
@ -1326,13 +1325,6 @@
|
|||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"description": "Taxes paid while advance payment will be adjusted against this invoice",
|
|
||||||
"fieldname": "adjust_advance_taxes",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Adjust Advance Taxes"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.is_internal_supplier",
|
"depends_on": "eval:doc.is_internal_supplier",
|
||||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||||
@ -1378,7 +1370,7 @@
|
|||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-09 21:12:30.422084",
|
"modified": "2021-03-30 22:45:58.334107",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
var globalOnload = frappe.listview_settings['Sales Invoice'].onload;
|
var globalOnload = frappe.listview_settings['Sales Invoice'].onload;
|
||||||
frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
frappe.listview_settings['Sales Invoice'].onload = function (list_view) {
|
||||||
|
|
||||||
// Provision in case onload event is added to sales_invoice.js in future
|
// Provision in case onload event is added to sales_invoice.js in future
|
||||||
if (globalOnload) {
|
if (globalOnload) {
|
||||||
globalOnload(doclist);
|
globalOnload(list_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = () => {
|
const action = () => {
|
||||||
const selected_docs = doclist.get_checked_items();
|
const selected_docs = list_view.get_checked_items();
|
||||||
const docnames = doclist.get_checked_items(true);
|
const docnames = list_view.get_checked_items(true);
|
||||||
|
|
||||||
for (let doc of selected_docs) {
|
for (let doc of selected_docs) {
|
||||||
if (doc.docstatus !== 1) {
|
if (doc.docstatus !== 1) {
|
||||||
@ -19,7 +19,7 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
args: {
|
args: {
|
||||||
'dt': doclist.doctype,
|
'dt': list_view.doctype,
|
||||||
'dn': docnames
|
'dn': docnames
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
@ -35,5 +35,140 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
|
list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
|
||||||
|
|
||||||
|
const generate_irns = () => {
|
||||||
|
const docnames = list_view.get_checked_items(true);
|
||||||
|
if (docnames && docnames.length) {
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices',
|
||||||
|
args: { docnames },
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Generating E-Invoices...')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frappe.msgprint({
|
||||||
|
message: __('Please select at least one sales invoice to generate IRN'),
|
||||||
|
title: __('No Invoice Selected'),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel_irns = () => {
|
||||||
|
const docnames = list_view.get_checked_items(true);
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
"label": "Reason",
|
||||||
|
"fieldname": "reason",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"reqd": 1,
|
||||||
|
"default": "1-Duplicate",
|
||||||
|
"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Remark",
|
||||||
|
"fieldname": "remark",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const d = new frappe.ui.Dialog({
|
||||||
|
title: __("Cancel IRN"),
|
||||||
|
fields: fields,
|
||||||
|
primary_action: function() {
|
||||||
|
const data = d.get_values();
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.regional.india.e_invoice.utils.cancel_irns',
|
||||||
|
args: {
|
||||||
|
doctype: list_view.doctype,
|
||||||
|
docnames,
|
||||||
|
reason: data.reason.split('-')[0],
|
||||||
|
remark: data.remark
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Cancelling E-Invoices...'),
|
||||||
|
});
|
||||||
|
d.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __('Submit')
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
let einvoicing_enabled = false;
|
||||||
|
frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => {
|
||||||
|
einvoicing_enabled = enabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
list_view.$result.on("change", "input[type=checkbox]", () => {
|
||||||
|
if (einvoicing_enabled) {
|
||||||
|
const docnames = list_view.get_checked_items(true);
|
||||||
|
// show/hide e-invoicing actions when no sales invoices are checked
|
||||||
|
if (docnames && docnames.length) {
|
||||||
|
// prevent adding actions twice if e-invoicing action group already exists
|
||||||
|
if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) {
|
||||||
|
list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing'));
|
||||||
|
list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing'));
|
||||||
|
list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.realtime.on("bulk_einvoice_generation_complete", (data) => {
|
||||||
|
const { failures, user, invoices } = data;
|
||||||
|
|
||||||
|
if (invoices.length != failures.length) {
|
||||||
|
frappe.msgprint({
|
||||||
|
message: __('{0} e-invoices generated successfully', [invoices.length]),
|
||||||
|
title: __('Bulk E-Invoice Generation Complete'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures && failures.length && user == frappe.session.user) {
|
||||||
|
let message = `
|
||||||
|
Failed to generate IRNs for following ${failures.length} sales invoices:
|
||||||
|
<ul style="padding-left: 20px; padding-top: 5px;">
|
||||||
|
${failures.map(d => `<li>${d.docname}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
frappe.msgprint({
|
||||||
|
message: message,
|
||||||
|
title: __('Bulk E-Invoice Generation Complete'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => {
|
||||||
|
const { failures, user, invoices } = data;
|
||||||
|
|
||||||
|
if (invoices.length != failures.length) {
|
||||||
|
frappe.msgprint({
|
||||||
|
message: __('{0} e-invoices cancelled successfully', [invoices.length]),
|
||||||
|
title: __('Bulk E-Invoice Cancellation Complete'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures && failures.length && user == frappe.session.user) {
|
||||||
|
let message = `
|
||||||
|
Failed to cancel IRNs for following ${failures.length} sales invoices:
|
||||||
|
<ul style="padding-left: 20px; padding-top: 5px;">
|
||||||
|
${failures.map(d => `<li>${d.docname}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
frappe.msgprint({
|
||||||
|
message: message,
|
||||||
|
title: __('Bulk E-Invoice Cancellation Complete'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
// print heading
|
|
||||||
cur_frm.pformat.print_heading = 'Invoice';
|
|
||||||
|
|
||||||
{% include 'erpnext/selling/sales_common.js' %};
|
{% include 'erpnext/selling/sales_common.js' %};
|
||||||
frappe.provide("erpnext.accounts");
|
frappe.provide("erpnext.accounts");
|
||||||
|
|
||||||
@ -916,7 +913,7 @@ frappe.ui.form.on('Sales Invoice Timesheet', {
|
|||||||
},
|
},
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
if(r.message){
|
if(r.message){
|
||||||
data = r.message;
|
let data = r.message;
|
||||||
frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
|
frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
|
||||||
frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
|
frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
|
||||||
frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
|
frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
|
||||||
|
@ -24,6 +24,7 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
|||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
from frappe.model.utils import get_fetch_values
|
from frappe.model.utils import get_fetch_values
|
||||||
from frappe.contacts.doctype.address.address import get_address_display
|
from frappe.contacts.doctype.address.address import get_address_display
|
||||||
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
|
|
||||||
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
||||||
|
|
||||||
@ -211,6 +212,9 @@ class SalesInvoice(SellingController):
|
|||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
|
if self.update_stock == 1:
|
||||||
|
self.repost_future_sle_and_gle()
|
||||||
|
|
||||||
if self.update_stock == 1:
|
if self.update_stock == 1:
|
||||||
self.repost_future_sle_and_gle()
|
self.repost_future_sle_and_gle()
|
||||||
|
|
||||||
|
@ -1166,10 +1166,12 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_create_so_with_margin(self):
|
def test_create_so_with_margin(self):
|
||||||
si = create_sales_invoice(item_code="_Test Item", qty=1, do_not_submit=True)
|
si = create_sales_invoice(item_code="_Test Item", qty=1, do_not_submit=True)
|
||||||
price_list_rate = 100
|
price_list_rate = flt(100) * flt(si.plc_conversion_rate)
|
||||||
si.items[0].price_list_rate = price_list_rate
|
si.items[0].price_list_rate = price_list_rate
|
||||||
si.items[0].margin_type = 'Percentage'
|
si.items[0].margin_type = 'Percentage'
|
||||||
si.items[0].margin_rate_or_amount = 25
|
si.items[0].margin_rate_or_amount = 25
|
||||||
|
si.items[0].discount_amount = 0.0
|
||||||
|
si.items[0].discount_percentage = 0.0
|
||||||
si.save()
|
si.save()
|
||||||
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
||||||
|
|
||||||
|
@ -406,9 +406,10 @@ def check_if_advance_entry_modified(args):
|
|||||||
throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
|
throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
|
||||||
|
|
||||||
def validate_allocated_amount(args):
|
def validate_allocated_amount(args):
|
||||||
|
precision = args.get('precision') or frappe.db.get_single_value("System Settings", "currency_precision")
|
||||||
if args.get("allocated_amount") < 0:
|
if args.get("allocated_amount") < 0:
|
||||||
throw(_("Allocated amount cannot be negative"))
|
throw(_("Allocated amount cannot be negative"))
|
||||||
elif args.get("allocated_amount") > args.get("unadjusted_amount"):
|
elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
|
||||||
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
||||||
|
|
||||||
def update_reference_in_journal_entry(d, jv_obj):
|
def update_reference_in_journal_entry(d, jv_obj):
|
||||||
|
@ -443,6 +443,16 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "GL Entry",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "UAE VAT 201",
|
||||||
|
"link_to": "UAE VAT 201",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
"po_required",
|
"po_required",
|
||||||
"pr_required",
|
"pr_required",
|
||||||
"maintain_same_rate",
|
"maintain_same_rate",
|
||||||
|
"maintain_same_rate_action",
|
||||||
|
"role_to_override_stop_action",
|
||||||
"allow_multiple_items",
|
"allow_multiple_items",
|
||||||
"subcontract",
|
"subcontract",
|
||||||
"backflush_raw_materials_of_subcontract_based_on",
|
"backflush_raw_materials_of_subcontract_based_on",
|
||||||
@ -89,6 +91,23 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_11",
|
"fieldname": "column_break_11",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Stop",
|
||||||
|
"depends_on": "maintain_same_rate",
|
||||||
|
"description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.",
|
||||||
|
"fieldname": "maintain_same_rate_action",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Action If Same Rate is Not Maintained",
|
||||||
|
"mandatory_depends_on": "maintain_same_rate",
|
||||||
|
"options": "Stop\nWarn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.maintain_same_rate_action == 'Stop'",
|
||||||
|
"fieldname": "role_to_override_stop_action",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Role Allowed to Override Stop Action",
|
||||||
|
"options": "Role"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
@ -96,7 +115,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-02 17:34:04.190677",
|
"modified": "2021-04-04 20:01:44.087066",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Buying Settings",
|
"name": "Buying Settings",
|
||||||
|
7
erpnext/change_log/v13/v13.0.2.md
Normal file
7
erpnext/change_log/v13/v13.0.2.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## Version 13.0.2 Release Notes
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- fix: frappe.whitelist for doc methods ([#25231](https://github.com/frappe/erpnext/pull/25231))
|
||||||
|
- fix: incorrect incoming rate for the sales return ([#25306](https://github.com/frappe/erpnext/pull/25306))
|
||||||
|
- fix(e-invoicing): validations & tax calculation fixes ([#25314](https://github.com/frappe/erpnext/pull/25314))
|
||||||
|
- fix: update scheduler check time ([#25295](https://github.com/frappe/erpnext/pull/25295))
|
471
erpnext/change_log/v13/v13_0_0.md
Normal file
471
erpnext/change_log/v13/v13_0_0.md
Normal file
@ -0,0 +1,471 @@
|
|||||||
|
# Version 13.0.0 Release Notes
|
||||||
|
|
||||||
|
### Accounting
|
||||||
|
- [New and refreshed POS](https://github.com/frappe/erpnext/pull/20789)
|
||||||
|
- [GST E-invoicing for India](https://docs.erpnext.com/docs/user/manual/en/regional/india/setup-e-invoicing)
|
||||||
|
- [Distributed Cost Center](https://docs.erpnext.com/docs/user/manual/en/accounts/distributed-cost-center)
|
||||||
|
- [Process Bulk Statement Of Accounts](https://docs.erpnext.com/docs/user/manual/en/accounts/process-statement-of-accounts)
|
||||||
|
- [More controlled deferred revenue booking](https://docs.erpnext.com/docs/user/manual/en/accounts/process-deferred-accounting)
|
||||||
|
- [Dunning](https://docs.erpnext.com/docs/user/manual/en/accounts/dunning)
|
||||||
|
- [Journal Entry Template](https://docs.erpnext.com/docs/user/manual/en/accounts/journal-entry-template)
|
||||||
|
- [POS Register report](https://github.com/frappe/erpnext/pull/23313)
|
||||||
|
- [UAE VAT 201 Report](https://github.com/frappe/erpnext/pull/23447)
|
||||||
|
|
||||||
|
|
||||||
|
### Loan Management
|
||||||
|
- [Loan Application](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-application)
|
||||||
|
- [Loan](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan)
|
||||||
|
- [Loan Security Pledge](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-security-pledge)
|
||||||
|
- [Loan Disbursement](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-disbursement)
|
||||||
|
- [Loan Repayment](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-repayment)
|
||||||
|
- [Loan Interest Accrual](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-interest-accrual)
|
||||||
|
- [Loan Write Off](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-write-off)
|
||||||
|
|
||||||
|
### Healthcare
|
||||||
|
- [Refactored Healthcare Module](https://docs.erpnext.com/docs/user/manual/en/healthcare)
|
||||||
|
- [Rehabilitation Module](https://docs.erpnext.com/docs/user/manual/en/healthcare/exercise_type)
|
||||||
|
- [Laboratory Module](https://docs.erpnext.com/docs/user/manual/en/healthcare/setup_laboratory)
|
||||||
|
- [Patient Progress Page](https://github.com/frappe/erpnext/pull/22474)
|
||||||
|
- [Inpatient Medication Order and Entry](https://docs.erpnext.com/docs/user/manual/en/healthcare/inpatient_medication_entry)
|
||||||
|
- [Therapy Plan Template](https://docs.erpnext.com/docs/user/manual/en/healthcare/therapy_plan)
|
||||||
|
- [Multi company support in Healthcare](https://github.com/frappe/erpnext/pull/21290)
|
||||||
|
- [Inpatient Medication Orders Script Report](https://github.com/frappe/erpnext/pull/23984)
|
||||||
|
- [Patient History Enhancements](https://github.com/frappe/erpnext/pull/24033)
|
||||||
|
|
||||||
|
|
||||||
|
### Stock
|
||||||
|
- [Putaway](https://docs.erpnext.com/docs/user/manual/en/stock/putaway-rule)
|
||||||
|
- [More accurate stock valuation in case of back-dated stock transactions](https://github.com/frappe/erpnext/pull/24183)
|
||||||
|
- [Repost item costing via background job](https://github.com/frappe/erpnext/pull/24183)
|
||||||
|
- [Item valuation for internal stock transfers](https://github.com/frappe/erpnext/pull/24200)
|
||||||
|
- [Multi currency in Landed Cost Voucher](https://github.com/frappe/erpnext/pull/24127)
|
||||||
|
- [Formula based Quality Inspection](https://docs.erpnext.com/docs/user/manual/en/stock/quality-inspection)
|
||||||
|
- [Value Based and Numeric Quality Inspection](https://github.com/frappe/erpnext/pull/24181)
|
||||||
|
- [Shipment](https://github.com/frappe/erpnext/pull/22914)
|
||||||
|
- [Return tracking in PR/DN](https://github.com/frappe/erpnext/pull/22859)
|
||||||
|
|
||||||
|
### Manufacturing
|
||||||
|
- [Production forecasting using Exponential Smoothing method](https://docs.erpnext.com/docs/user/manual/en/manufacturing/reports/demand-driven-forecasting)
|
||||||
|
- [BOM Template](https://docs.erpnext.com/docs/user/manual/en/manufacturing/bill-of-materials#34-bom-template)
|
||||||
|
- [Downtime Entry](https://docs.erpnext.com/docs/user/manual/en/manufacturing/downtime-entry)
|
||||||
|
- [Quality Inspection on Job Card](https://github.com/frappe/erpnext/pull/23964)
|
||||||
|
- New Reports
|
||||||
|
- Production Planning Report ([#21763](https://github.com/frappe/erpnext/pull/21763))
|
||||||
|
- BOM Operations Time ([#21763](https://github.com/frappe/erpnext/pull/21763))
|
||||||
|
- Work Order Summary ([#21430](https://github.com/frappe/erpnext/pull/21430))
|
||||||
|
- Job card Summary ([#21430](https://github.com/frappe/erpnext/pull/21430))
|
||||||
|
- Downtime Analysis ([#21430](https://github.com/frappe/erpnext/pull/21430))
|
||||||
|
- Quality Inspection ([#21430](https://github.com/frappe/erpnext/pull/21430))
|
||||||
|
|
||||||
|
### HR
|
||||||
|
- [Leave policy assignment](https://github.com/frappe/erpnext/pull/23112)
|
||||||
|
- [In and Out time in attendance](https://github.com/frappe/erpnext/pull/21547)
|
||||||
|
- [Shift management](https://docs.erpnext.com/docs/user/manual/en/human-resources/shift-management)
|
||||||
|
- [Recruitment analytics](https://github.com/frappe/erpnext/pull/21732)
|
||||||
|
- [Bulk Mark Attendance](https://github.com/frappe/erpnext/pull/20062)
|
||||||
|
- [Leave type with partial payment](https://github.com/frappe/erpnext/pull/23173)
|
||||||
|
- New and enhanced reports
|
||||||
|
- Employee Analytics ([#21705](https://github.com/frappe/erpnext/pull/21705))
|
||||||
|
- Employee Leave Balance ([#20754](https://github.com/frappe/erpnext/pull/20754))
|
||||||
|
- Employee Leave Balance Summary ([#20754](https://github.com/frappe/erpnext/pull/20754))
|
||||||
|
|
||||||
|
### Payroll
|
||||||
|
- [Multi-currency payroll](https://github.com/frappe/erpnext/pull/23519)
|
||||||
|
- [Payroll based on attendance](https://github.com/frappe/erpnext/pull/21258)
|
||||||
|
- [Payroll based on employee cost center](https://github.com/frappe/erpnext/pull/21609)
|
||||||
|
- [Recurring Additional Salary](https://github.com/frappe/erpnext/pull/20936)
|
||||||
|
- [Compute Year to Date for Salary Slip components](https://github.com/frappe/erpnext/pull/24362)
|
||||||
|
- New Reports
|
||||||
|
- Income Tax Deductions
|
||||||
|
- Professional Tax Deductions
|
||||||
|
- Provident Fund Deductions
|
||||||
|
- Total Salary Payments Based on Payment Mode
|
||||||
|
- Salary Payments via ECS
|
||||||
|
|
||||||
|
### CRM
|
||||||
|
- [Social Media Post](https://docs.erpnext.com/docs/user/manual/en/CRM/social-media-post)
|
||||||
|
- [Make Quotation against Blanket Order](https://docs.erpnext.com/docs/user/manual/en/selling/blanket-order)
|
||||||
|
- [Calendar View for Opportunity](https://github.com/frappe/erpnext/pull/21280)
|
||||||
|
|
||||||
|
### Selling
|
||||||
|
- [Batch wise item pricing](https://github.com/frappe/erpnext/pull/24470)
|
||||||
|
- [Refreshed shopping cart](https://github.com/frappe/erpnext/pull/22617)
|
||||||
|
- [Territory-wise Sales Report](https://github.com/frappe/erpnext/pull/20428)
|
||||||
|
|
||||||
|
#### Buying
|
||||||
|
- [Multi UOM support in Request for Quotation](https://github.com/frappe/erpnext/pull/22249)
|
||||||
|
- [Provision to make RFQ against Opportunity](https://github.com/frappe/erpnext/pull/22765)
|
||||||
|
- [Item Rate in Stock UOM in purchase cycle](https://github.com/frappe/erpnext/pull/24315)
|
||||||
|
- New Reports
|
||||||
|
- Requested Items To Order ([#21611](https://github.com/frappe/erpnext/pull/21611))
|
||||||
|
- Purchase Order Analysis ([#21611](https://github.com/frappe/erpnext/pull/21611))
|
||||||
|
- Supplier Quotation Comparison report ([#23323](https://github.com/frappe/erpnext/pull/23323))
|
||||||
|
|
||||||
|
### Project
|
||||||
|
- [Project template with dependent tasks](https://github.com/frappe/erpnext/pull/24092)
|
||||||
|
- [Project Summary Report](https://github.com/frappe/erpnext/pull/21587)
|
||||||
|
|
||||||
|
### Support
|
||||||
|
- [Help Articles on support portal](https://github.com/frappe/erpnext/pull/22194)
|
||||||
|
- [Issue Metrics and SLA Enhancements](https://github.com/frappe/erpnext/pull/21617)
|
||||||
|
- [Issue Summary Script Report](https://docs.erpnext.com/docs/user/manual/en/support/support_reports)
|
||||||
|
- [Issue Analytics Script Report](https://docs.erpnext.com/docs/user/manual/en/support/support_reports)
|
||||||
|
|
||||||
|
### Non-Profits
|
||||||
|
- [80G Certificates and Donations](https://docs.erpnext.com/docs/user/manual/en/non_profit/tax_exemption_80g_certificate)
|
||||||
|
|
||||||
|
#### Integrations
|
||||||
|
- [Woocommerce Integration](https://docs.erpnext.com/docs/user/manual/en/erpnext_integration/woocommerce_integration)
|
||||||
|
- [Taxjar Integration](https://github.com/frappe/erpnext/pull/21047)
|
||||||
|
- [M-pesa Integration](https://docs.erpnext.com/docs/user/manual/en/erpnext_integration/mpesa-integration)
|
||||||
|
- [Telephony feature using Twillio](https://github.com/frappe/erpnext/pull/24032)
|
||||||
|
- [Voice Call Settings](https://github.com/frappe/erpnext/pull/24126)
|
||||||
|
|
||||||
|
|
||||||
|
#### Other Enhancements and Fixes
|
||||||
|
- Accounting Dimensions in Budget Variance Report ([#19973](https://github.com/frappe/erpnext/pull/19973))
|
||||||
|
- "Sync Now" option in Plaid Settings ([#23602](https://github.com/frappe/erpnext/pull/23602))
|
||||||
|
- Custom Fields in POS ([#19876](https://github.com/frappe/erpnext/pull/19876))
|
||||||
|
- [Inter Warehouse Stock Transfer in Purchase Receipt](https://docs.erpnext.com/docs/user/manual/en/stock/articles/material-transfer-from-delivery-note)
|
||||||
|
- [Accounts Payable Report based on Payment Terms](https://docs.erpnext.com/docs/user/manual/en/accounts/accounting-reports)
|
||||||
|
- Configurable accounting dimension filters and validations ([#23912](https://github.com/frappe/erpnext/pull/23912))
|
||||||
|
- Territory tree in Customer Acquisition and Loyalty report ([#21668](https://github.com/frappe/erpnext/pull/21668))
|
||||||
|
- Allow Purchase Invoice Creation Without Purchase Order Checkbox in Supplier ([#20864](https://github.com/frappe/erpnext/pull/20864))
|
||||||
|
- Gross Profit In Quotation ([#21795](https://github.com/frappe/erpnext/pull/21795))
|
||||||
|
- Notify credit controller users for credit limit extension via Email ([#22213](https://github.com/frappe/erpnext/pull/22213))
|
||||||
|
- Run MRP at parent level in the production plan and make material transfer based upon materials availability ([#21545](https://github.com/frappe/erpnext/pull/21545))
|
||||||
|
- Balance Serial Nos in Stock Ledger report ([#23675](https://github.com/frappe/erpnext/pull/23675))
|
||||||
|
- Youtube interactions via Video ([#22867](https://github.com/frappe/erpnext/pull/22867))
|
||||||
|
- Consider Holiday List in Student Leave Application and Attendance ([#23388](https://github.com/frappe/erpnext/pull/23388))
|
||||||
|
- Patient appointment status changes ([#24201](https://github.com/frappe/erpnext/pull/24201))
|
||||||
|
- Sales order status filter added for production plan ([#23805](https://github.com/frappe/erpnext/pull/23805))
|
||||||
|
- Monthly attendance sheet report group by Department, Designation, Employee Grade and Branch ([#21331](https://github.com/frappe/erpnext/pull/21331))
|
||||||
|
- Upload Attendance template now have pre-filled holiday status ([#20947](https://github.com/frappe/erpnext/pull/20947))
|
||||||
|
- Provision to disable serial no and batch selector ([#24398](https://github.com/frappe/erpnext/pull/24398))
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>More</summary>
|
||||||
|
|
||||||
|
- Fetch Items from BOM in Stock Entry([#19498](https://github.com/frappe/erpnext/pull/19498))
|
||||||
|
- Supplier Sourced Items in BOM ([#23557](https://github.com/frappe/erpnext/pull/23557))
|
||||||
|
- Close Production Plan ([#23728](https://github.com/frappe/erpnext/pull/23728))
|
||||||
|
- Button to create Stock Entry for Drug Shortage ([#24012](https://github.com/frappe/erpnext/pull/24012))
|
||||||
|
- Added column cost center in Accounts Receivable report ([#23835](https://github.com/frappe/erpnext/pull/23835))
|
||||||
|
- Added jinja templating in Contract Template ([#24046](https://github.com/frappe/erpnext/pull/24046))
|
||||||
|
- Make account number length configurable ([#23845](https://github.com/frappe/erpnext/pull/23845))
|
||||||
|
- Add company and correct filter in bank reconciliation statement ([#23614](https://github.com/frappe/erpnext/pull/23614))
|
||||||
|
- Added Condition field in Pricing Rule ([#23014](https://github.com/frappe/erpnext/pull/23014))
|
||||||
|
- Open lead status on next contact date ([#23445](https://github.com/frappe/erpnext/pull/23445))
|
||||||
|
- [Tax Category in POS Profile](https://docs.erpnext.com/docs/user/manual/en/accounts/pos-profile)
|
||||||
|
- Added phone field in product Inquiry ([#23170](https://github.com/frappe/erpnext/pull/23170))
|
||||||
|
- Allow Discharge despite Unbilled Healthcare Services ([#24281](https://github.com/frappe/erpnext/pull/24281))
|
||||||
|
- Do Not Bill Patient Encounters for Inpatients ([#24355](https://github.com/frappe/erpnext/pull/24355))
|
||||||
|
- Autofill Supplier pop-up when only 1 Supplier in RFQ ([#22512](https://github.com/frappe/erpnext/pull/22512))
|
||||||
|
- Accounting entries for service item in Purchase receipt ([#22223](https://github.com/frappe/erpnext/pull/22223))
|
||||||
|
- Added Project in Sales Analytics report ([#23309](https://github.com/frappe/erpnext/pull/23309))
|
||||||
|
- Added all companies option in employee tree to view employee across all companies ([#22573](https://github.com/frappe/erpnext/pull/22573))
|
||||||
|
- Email Group Option In Email Campaign ([#22731](https://github.com/frappe/erpnext/pull/22731))
|
||||||
|
- Stock Report Enhancements ([#21727](https://github.com/frappe/erpnext/pull/21727))
|
||||||
|
- Added range for age in stock ageing ([#22622](https://github.com/frappe/erpnext/pull/22622))
|
||||||
|
- Report Summary in Financial Statement([#20876](https://github.com/frappe/erpnext/pull/20876))
|
||||||
|
- Added sequence id in routing for the completion of operations sequentially ([#23641](https://github.com/frappe/erpnext/pull/23641))
|
||||||
|
- Nested Set filtering for Accounting Dimension
|
||||||
|
- Add/Remove Items from submitted Sales/Purchase Order
|
||||||
|
- Provision to edit Item Details from Marketplace
|
||||||
|
- Scan Barcode in Purchase Receipt
|
||||||
|
- Disable Rounded Totals Checkbox for Salary Slips in HR Settings
|
||||||
|
|
||||||
|
- Renamed Loan Management to Loan on Desk Page ([#21877](https://github.com/frappe/erpnext/pull/21877))
|
||||||
|
- Added Expense Approver field in Employee master ([#22244](https://github.com/frappe/erpnext/pull/22244))
|
||||||
|
- Bill all hours by default on Timesheet ([#22155](https://github.com/frappe/erpnext/pull/22155))
|
||||||
|
- Unable to cancel employee advance ([#22374](https://github.com/frappe/erpnext/pull/22374))
|
||||||
|
- Status error in purchase invoice ([#22351](https://github.com/frappe/erpnext/pull/22351))
|
||||||
|
- Item-wise sales and purchase register export ([#22184](https://github.com/frappe/erpnext/pull/22184))
|
||||||
|
- Billing address in for Purchase documents ([#22233](https://github.com/frappe/erpnext/pull/22233))
|
||||||
|
- Handle canceled entries in financial statements ([#22231](https://github.com/frappe/erpnext/pull/22231))
|
||||||
|
- Default period start date and period end date for financial statements ([#22011](https://github.com/frappe/erpnext/pull/22011))
|
||||||
|
- Update Packed Items via Update Items in Sales Order ([#22392](https://github.com/frappe/erpnext/pull/22392))
|
||||||
|
- Hide delete company transactions button if not system manager ([#21839](https://github.com/frappe/erpnext/pull/21839))
|
||||||
|
- Skipping total row for tree-view reports ([#22350](https://github.com/frappe/erpnext/pull/22350))
|
||||||
|
- Cancelled entries in tds payable monthly report ([#22131](https://github.com/frappe/erpnext/pull/22131))
|
||||||
|
- Inter-company Invoice currency for multicurrency transactions ([#21984](https://github.com/frappe/erpnext/pull/21984))
|
||||||
|
- Filter batches based on item and warehouse in Pick List (develop) ([#21780](https://github.com/frappe/erpnext/pull/21780))
|
||||||
|
- Set cost center in Expense Claim child based on parent (if missing) ([#22175](https://github.com/frappe/erpnext/pull/22175))
|
||||||
|
- Item wise backdated stock entry posting for immutable ledger ([#22366](https://github.com/frappe/erpnext/pull/22366))
|
||||||
|
- Shopping cart UI fixes ([#22137](https://github.com/frappe/erpnext/pull/22137))
|
||||||
|
- Filter Leave Type based on allocation for a particular employee ([#22050](https://github.com/frappe/erpnext/pull/22050))
|
||||||
|
- Party validation for inter-warehouse transaction ([#22186](https://github.com/frappe/erpnext/pull/22186))
|
||||||
|
- Manufacturing dashboard and work order summary chart ([#21946](https://github.com/frappe/erpnext/pull/21946))
|
||||||
|
- IP Admission and Discharge, Minor fixes ([#21817](https://github.com/frappe/erpnext/pull/21817))
|
||||||
|
- Validation of Purchase Order against Material Request missing ([#22192](https://github.com/frappe/erpnext/pull/22192))
|
||||||
|
- Staffing Plan validation ([#22379](https://github.com/frappe/erpnext/pull/22379))
|
||||||
|
- Do not allow backdated stock transactions in previous fiscal year ([#21967](https://github.com/frappe/erpnext/pull/21967))
|
||||||
|
- Employee Advance Return not working ([#21812](https://github.com/frappe/erpnext/pull/21812))
|
||||||
|
- Added card for reports on education desk ([#21853](https://github.com/frappe/erpnext/pull/21853))
|
||||||
|
- Refactored project summary report ([#21943](https://github.com/frappe/erpnext/pull/21943))
|
||||||
|
- Revenue and Customer Count only in date range in Customer Acquitition Report ([#22210](https://github.com/frappe/erpnext/pull/22210))
|
||||||
|
- Alternative item not working for subcontract ([#22386](https://github.com/frappe/erpnext/pull/22386))
|
||||||
|
- Unable to create batched Item ([#22393](https://github.com/frappe/erpnext/pull/22393))
|
||||||
|
- Filters for the manufacturing reports ([#21960](https://github.com/frappe/erpnext/pull/21960))
|
||||||
|
- Raw material warehouse in Production Planning Report ([#21982](https://github.com/frappe/erpnext/pull/21982))
|
||||||
|
- Allowed LWP leave types to select in Leave Application even if there is no allocation against them ([#22197](https://github.com/frappe/erpnext/pull/22197))
|
||||||
|
- Report not working on parameter Grade ([#21951](https://github.com/frappe/erpnext/pull/21951))
|
||||||
|
- Allow to enter Relieving date if employee status is Left ([#22242](https://github.com/frappe/erpnext/pull/22242))
|
||||||
|
- Resetting lost reason in opportunity and quotation ([#22378](https://github.com/frappe/erpnext/pull/22378))
|
||||||
|
- Filtering issues in opening invoice creation tool ([#21969](https://github.com/frappe/erpnext/pull/21969))
|
||||||
|
- Set default reference Id for "On Previous Row Amount" and "On Previous Row Total" ([#22346](https://github.com/frappe/erpnext/pull/22346))
|
||||||
|
- UX date range field separated in from and to date fields. ([#21765](https://github.com/frappe/erpnext/pull/21765))
|
||||||
|
- Enable show_configure_button when shopping cart is enabled ([#22468](https://github.com/frappe/erpnext/pull/22468))
|
||||||
|
- Setup status indicators for Job Offer and Job Applicant (develop) ([#22445](https://github.com/frappe/erpnext/pull/22445))
|
||||||
|
- Item-wise sales history report ([#22783](https://github.com/frappe/erpnext/pull/22783))
|
||||||
|
- Setting filter for project in kanban board ([#22717](https://github.com/frappe/erpnext/pull/22717))
|
||||||
|
- Dashboard For Timesheet ([#22750](https://github.com/frappe/erpnext/pull/22750))
|
||||||
|
- Handle custom statuses for the pause SLA configuration ([#22349](https://github.com/frappe/erpnext/pull/22349))
|
||||||
|
- Quality Feedback and Template ([#22571](https://github.com/frappe/erpnext/pull/22571))
|
||||||
|
- Unable to change link from new lead to existing customer ([#22787](https://github.com/frappe/erpnext/pull/22787))
|
||||||
|
- Move Issue List actions under 'Actions' dropdown (ux) ([#22710](https://github.com/frappe/erpnext/pull/22710))
|
||||||
|
- Cost center should only show option of selected company ([#22598](https://github.com/frappe/erpnext/pull/22598))
|
||||||
|
- Serial No Rename does not affect Stock Ledger Entry ([#22746](https://github.com/frappe/erpnext/pull/22746))
|
||||||
|
- Descriptions not copied while creating Fees from Fee Structure ([#22792](https://github.com/frappe/erpnext/pull/22792))
|
||||||
|
- Company filter for cost_center and expense_account in all sales and purchase transactions ([#22478](https://github.com/frappe/erpnext/pull/22478))
|
||||||
|
- Arrangements of filters for reports accounts payable & receivable ([#22636](https://github.com/frappe/erpnext/pull/22636))
|
||||||
|
- Update the project after task deletion so that the % completed shows correct value ([#22591](https://github.com/frappe/erpnext/pull/22591))
|
||||||
|
- Block Invalid Serial No updates in Maintenance Schedule ([#22665](https://github.com/frappe/erpnext/pull/22665))
|
||||||
|
- Fetch item price in sales invoice based on it's validity ([#22563](https://github.com/frappe/erpnext/pull/22563))
|
||||||
|
- Add view ledger button for cancelled docs ([#22432](https://github.com/frappe/erpnext/pull/22432))
|
||||||
|
- Allow creating SLA documents even if SLA tracking is not enabled ([#22608](https://github.com/frappe/erpnext/pull/22608))
|
||||||
|
- Quotation list view blank if quotation_to field not set as a standard filter ([#22672](https://github.com/frappe/erpnext/pull/22672))
|
||||||
|
- Salary deductions report fixes ([#22397](https://github.com/frappe/erpnext/pull/22397))
|
||||||
|
22727))
|
||||||
|
- Incorrect delivered qty in Supplier-Wise Sales Analytics ([#22631](https://github.com/frappe/erpnext/pull/22631))
|
||||||
|
- Moved parent warehouse to top section also added a section break ([#22708](https://github.com/frappe/erpnext/pull/22708))
|
||||||
|
- Skip Progress and Completed by fields on Task Duplication ([#22565](https://github.com/frappe/erpnext/pull/22565))
|
||||||
|
- Incorrect stock after merging the items ([#22526](https://github.com/frappe/erpnext/pull/22526))
|
||||||
|
- Letter head not found in opening invoice creation tool ([#22488](https://github.com/frappe/erpnext/pull/22488))
|
||||||
|
- Cannot cancel asset and asset movement ([#22441](https://github.com/frappe/erpnext/pull/22441))
|
||||||
|
- Fetch project-related info in Timesheet ([#22423](https://github.com/frappe/erpnext/pull/22423))
|
||||||
|
- Currency symbol not showing as per company currency in stock balance report ([#22724](https://github.com/frappe/erpnext/pull/22724))
|
||||||
|
- Add default cost center in payment reconciliation JV ([#22614](https://github.com/frappe/erpnext/pull/22614))
|
||||||
|
- Stock Reconciliation Invalid Quantity for Batched Item ([#22726](https://github.com/frappe/erpnext/pull/22726))
|
||||||
|
- Project link not set in accounts other than profit and loss accounts ([#22051](https://github.com/frappe/erpnext/pull/22051))
|
||||||
|
- Buying price for non stock item in gross profit report ([#22616](https://github.com/frappe/erpnext/pull/22616))
|
||||||
|
- Multi currency payment reconciliation ([#22738](https://github.com/frappe/erpnext/pull/22738))
|
||||||
|
- Cannot cancel assets with repair pending ([#22440](https://github.com/frappe/erpnext/pull/22440))
|
||||||
|
- Reset homepage to home after unchecking products page ([#22736](https://github.com/frappe/erpnext/pull/22736))
|
||||||
|
- Generic Message in previous doc validation for buying and selling ([#22546](https://github.com/frappe/erpnext/pull/22546))
|
||||||
|
- Expense claim outstanding while making payment entry ([#22735](https://github.com/frappe/erpnext/pull/22735))
|
||||||
|
- Take parent cost center for child if no cost center at child in expense claim ([#22496](https://github.com/frappe/erpnext/pull/22496))
|
||||||
|
- Consider company fiscal year for getting balance ([#22577](https://github.com/frappe/erpnext/pull/22577))
|
||||||
|
- Pick List empty table and Serial-Batch items handling ([#22426](https://github.com/frappe/erpnext/pull/22426))
|
||||||
|
- Show total row in print format of financial statement ([#22693](https://github.com/frappe/erpnext/pull/22693))
|
||||||
|
- Set Root as Parent if no parent in new tree view node ([#22497](https://github.com/frappe/erpnext/pull/22497))
|
||||||
|
- Multiple pos issues ([#23725](https://github.com/frappe/erpnext/pull/23725))
|
||||||
|
- Calculate taxes if tax is based on item quantity and inclusive on item price ([#23001](https://github.com/frappe/erpnext/pull/23001))
|
||||||
|
- Contact us button not visible in the website for the non variant items ([#23217](https://github.com/frappe/erpnext/pull/23217))
|
||||||
|
- Not able to make Material Request from Sales Order ([#23669](https://github.com/frappe/erpnext/pull/23669))
|
||||||
|
- Capture advance payments in payment order ([#23256](https://github.com/frappe/erpnext/pull/23256))
|
||||||
|
- Program and Course Enrollment fixes ([#23333](https://github.com/frappe/erpnext/pull/23333))
|
||||||
|
- Cannot create asset if cwip disabled and account not set ([#23580](https://github.com/frappe/erpnext/pull/23580))
|
||||||
|
- Cannot merge pos invoices with inclusive tax ([#23541](https://github.com/frappe/erpnext/pull/23541))
|
||||||
|
- Do not allow Company as accounting dimension ([#23755](https://github.com/frappe/erpnext/pull/23755))
|
||||||
|
- Set value of wrong Bank Account field in Payment Entry ([#22302](https://github.com/frappe/erpnext/pull/22302))
|
||||||
|
- Reverse journal entry for multi-currency ([#23165](https://github.com/frappe/erpnext/pull/23165))
|
||||||
|
- Updated integrations desk page ([#23772](https://github.com/frappe/erpnext/pull/23772))
|
||||||
|
- Assessment Result child table not visible when accessed via Assessment Plan dashboard ([#22880](https://github.com/frappe/erpnext/pull/22880))
|
||||||
|
- Conversion factor fixes in Stock Entry ([#23407](https://github.com/frappe/erpnext/pull/23407))
|
||||||
|
- Total calculations for multi-currency RCM invoices ([#23072](https://github.com/frappe/erpnext/pull/23072))
|
||||||
|
- Show accounts in financial statements upto level 20 ([#23718](https://github.com/frappe/erpnext/pull/23718))
|
||||||
|
- Consolidated financial statement sums values into wrong parent ([#23288](https://github.com/frappe/erpnext/pull/23288))
|
||||||
|
- Set SLA variance in seconds for Duration fieldtype ([#23765](https://github.com/frappe/erpnext/pull/23765))
|
||||||
|
- Added missing reports on selling desk ([#23548](https://github.com/frappe/erpnext/pull/23548))
|
||||||
|
- Fixed heading in the mobile view ([#23145](https://github.com/frappe/erpnext/pull/23145))
|
||||||
|
- Misleading filters on Item tax Template Link field ([#22918](https://github.com/frappe/erpnext/pull/22918))
|
||||||
|
- Do not consider opening entries for TDS calculation ([#23597](https://github.com/frappe/erpnext/pull/23597))
|
||||||
|
- Attendance calendar map fix ([#23245](https://github.com/frappe/erpnext/pull/23245))
|
||||||
|
- Post cancellation accounting entry on posting date instead of current ([#23361](https://github.com/frappe/erpnext/pull/23361))
|
||||||
|
- Set Customer only if Contact is present ([#23704](https://github.com/frappe/erpnext/pull/23704))
|
||||||
|
- Add Delivery Note Count in Sales Invoice Dashboard ([#23161](https://github.com/frappe/erpnext/pull/23161))
|
||||||
|
- Breadcrumbs for Maintenance Visit and Schedule ([#23369](https://github.com/frappe/erpnext/pull/23369))
|
||||||
|
- Raise Error on over receipt/consumption for sub-contracted PR ([#23195](https://github.com/frappe/erpnext/pull/23195))
|
||||||
|
- Validate if company not set in the Payment Entry ([#23419](https://github.com/frappe/erpnext/pull/23419))
|
||||||
|
- Ignore company and bank account doctype while deleting company transactions ([#22953](https://github.com/frappe/erpnext/pull/22953))
|
||||||
|
- Sales funnel data is inconsistent ([#23110](https://github.com/frappe/erpnext/pull/23110))
|
||||||
|
- Credit Limit Email not working ([#23059](https://github.com/frappe/erpnext/pull/23059))
|
||||||
|
- Add Company in list fields to fetch for Expense Claim ([#23007](https://github.com/frappe/erpnext/pull/23007))
|
||||||
|
- Issue form cleaned up and renamed Minutes to First Response field ([#23066](https://github.com/frappe/erpnext/pull/23066))
|
||||||
|
- Quotation lost reason options fix ([#22814](https://github.com/frappe/erpnext/pull/22814))
|
||||||
|
- Tax amounts in HSN Wise Outward summary ([#23076](https://github.com/frappe/erpnext/pull/23076))
|
||||||
|
- Patient Appointment not able to save ([#23434](https://github.com/frappe/erpnext/pull/23434))
|
||||||
|
- Removed Working Hours field from Company ([#23009](https://github.com/frappe/erpnext/pull/23009))
|
||||||
|
- Added check-in time validation in the Inpatient Record - Transfer ([#22958](https://github.com/frappe/erpnext/pull/22958))
|
||||||
|
- Handle Blank from/to range in Numeric Item Attribute ([#23483](https://github.com/frappe/erpnext/pull/23483))
|
||||||
|
- Sequence Matcher error in Bank Reconciliation ([#23539](https://github.com/frappe/erpnext/pull/23539))
|
||||||
|
- Fixed Conversion Factor rate for the BOM Exploded Item ([#23151](https://github.com/frappe/erpnext/pull/23151))
|
||||||
|
- Payment Schedule not fetching ([#23476](https://github.com/frappe/erpnext/pull/23476))
|
||||||
|
- Validate if removed Item Attributes exist in variant items ([#22911](https://github.com/frappe/erpnext/pull/22911))
|
||||||
|
- Set default billing address for purchase documents ([#22950](https://github.com/frappe/erpnext/pull/22950))
|
||||||
|
- Added help link in navbar settings ([#22943](https://github.com/frappe/erpnext/pull/22943))
|
||||||
|
- Apply TDS on Purchase Invoice creation from Purchase Order and Purchase Receipt ([#23282](https://github.com/frappe/erpnext/pull/23282))
|
||||||
|
- Education Module fixes ([#23714](https://github.com/frappe/erpnext/pull/23714))
|
||||||
|
- Filter out cancelled entries in customer ledger summary ([#23205](https://github.com/frappe/erpnext/pull/23205))
|
||||||
|
- Fiscal Year and Tax Rates for Italy ([#23623](https://github.com/frappe/erpnext/pull/23623))
|
||||||
|
- Production Plan incorrect Work Order qty ([#23264](https://github.com/frappe/erpnext/pull/23264))
|
||||||
|
- Added new filters in the Batch-wise Balance History report ([#23676](https://github.com/frappe/erpnext/pull/23676))
|
||||||
|
- Update state code and union territory for Daman and Diu ([#22988](https://github.com/frappe/erpnext/pull/22988))
|
||||||
|
- Set Stock UOM in item while creating Material Request from Stock Entry ([#23436](https://github.com/frappe/erpnext/pull/23436))
|
||||||
|
- Sales Order to Purchase Order flow improvement ([#23357](https://github.com/frappe/erpnext/pull/23357))
|
||||||
|
- Student Admission and Student Applicant fixes ([#23515](https://github.com/frappe/erpnext/pull/23515))
|
||||||
|
- Loan disbursement amount validation ([#24000](https://github.com/frappe/erpnext/pull/24000))
|
||||||
|
- Making company address read-only in delivery note ([#23890](https://github.com/frappe/erpnext/pull/23890))
|
||||||
|
- BOM stock report color showing always red ([#23994](https://github.com/frappe/erpnext/pull/23994))
|
||||||
|
- Added filter for customer field in Issue ([#24051](https://github.com/frappe/erpnext/pull/24051))
|
||||||
|
- Added project link in timesheet form ([#23764](https://github.com/frappe/erpnext/pull/23764))
|
||||||
|
- Update integrations desk page ([#23767](https://github.com/frappe/erpnext/pull/23767))
|
||||||
|
- Place of supply change on address change ([#23941](https://github.com/frappe/erpnext/pull/23941))
|
||||||
|
- TDS calculation, skip invoices with "Apply Tax Withholding Amount" has disabled ([#23672](https://github.com/frappe/erpnext/pull/23672))
|
||||||
|
- Auto fetch serial nos with modified conversion factor ([#23854](https://github.com/frappe/erpnext/pull/23854))
|
||||||
|
- Default cost center in item master not set in stock entry ([#23877](https://github.com/frappe/erpnext/pull/23877))
|
||||||
|
- Incorrect de-link serial no and batch ([#23947](https://github.com/frappe/erpnext/pull/23947))
|
||||||
|
- Accounting for internal transfer invoices within same company ([#24021](https://github.com/frappe/erpnext/pull/24021))
|
||||||
|
- Multiple pricing rule with margin type as Percentage is not working ([#24205](https://github.com/frappe/erpnext/pull/24205))
|
||||||
|
- Added Purchase Order to Global Search ([#24055](https://github.com/frappe/erpnext/pull/24055))
|
||||||
|
- Cannot expand row in update items dialog ([#23839](https://github.com/frappe/erpnext/pull/23839))
|
||||||
|
- Maintain stock can't be changed it there is product bundle ([#23989](https://github.com/frappe/erpnext/pull/23989))
|
||||||
|
- SO to PO Mapping Issue ([#23820](https://github.com/frappe/erpnext/pull/23820))
|
||||||
|
- Asset with value zero doesn't show up in fixed asset register ([#24091](https://github.com/frappe/erpnext/pull/24091))
|
||||||
|
- Cannot save customer email & phone ([#23797](https://github.com/frappe/erpnext/pull/23797))
|
||||||
|
- Incorrect balance value in stock balance report ([#24048](https://github.com/frappe/erpnext/pull/24048))
|
||||||
|
- Payment Terms not fetched in Purchase Invoice from Purchase Receipt ([#23735](https://github.com/frappe/erpnext/pull/23735))
|
||||||
|
- Fix for LMS Sign Up link ([#23743](https://github.com/frappe/erpnext/pull/23743))
|
||||||
|
- Incorrect stock quantity if 'Allow Multiple Material Consumption… ([#24116](https://github.com/frappe/erpnext/pull/24116))
|
||||||
|
- Added wrong absent days calculation in salary slip ([#23897](https://github.com/frappe/erpnext/pull/23897))
|
||||||
|
- Purchase receipt to purchase invoice bill date mapping ([#23967](https://github.com/frappe/erpnext/pull/23967))
|
||||||
|
- Overriding po ([#24022](https://github.com/frappe/erpnext/pull/24022))
|
||||||
|
- Do not cancel reference document on Quality Inspection cancellation ([#24198](https://github.com/frappe/erpnext/pull/24198))
|
||||||
|
- Get formatted value in 'taxes' print template ([#24035](https://github.com/frappe/erpnext/pull/24035))
|
||||||
|
- Don't overrule Item Price via Pricing Rule Rate if 0 ([#23636](https://github.com/frappe/erpnext/pull/23636))
|
||||||
|
- Job card error handling for operations field ([#23991](https://github.com/frappe/erpnext/pull/23991))
|
||||||
|
- Validation for journal entry with 0 debit and credit values ([#23975](https://github.com/frappe/erpnext/pull/23975))
|
||||||
|
- Check if customer exists in product listing ([#24030](https://github.com/frappe/erpnext/pull/24030))
|
||||||
|
- Asset finance book posting date fix ([#23778](https://github.com/frappe/erpnext/pull/23778))
|
||||||
|
- Same source and target tables in Status Updater's update query ([#24110](https://github.com/frappe/erpnext/pull/24110))
|
||||||
|
- Asset finance book depreciation posting date fix ([#23833](https://github.com/frappe/erpnext/pull/23833))
|
||||||
|
- Ignore exception during leave ledger creation from patch ([#24005](https://github.com/frappe/erpnext/pull/24005))
|
||||||
|
- Added link of bank reconciliation and clearance in accounting desk page ([#23850](https://github.com/frappe/erpnext/pull/23850))
|
||||||
|
- Sales invoice add button from sales order dashboard ([#24077](https://github.com/frappe/erpnext/pull/24077))
|
||||||
|
- Incorrect calculation for consumed qty for subcontract item ([#23257](https://github.com/frappe/erpnext/pull/23257))
|
||||||
|
- Incorrect required_qty in Production Planning Report ([#24074](https://github.com/frappe/erpnext/pull/24074))
|
||||||
|
- Email digest user not found ([#23949](https://github.com/frappe/erpnext/pull/23949))
|
||||||
|
- Delete Receive at Warehouse entry on cancellation of Send to War… ([#24115](https://github.com/frappe/erpnext/pull/24115))
|
||||||
|
- Added TDS Payable account number and an error message ([#24065](https://github.com/frappe/erpnext/pull/24065))
|
||||||
|
- Override field_map for job card gantt ([#24155](https://github.com/frappe/erpnext/pull/24155))
|
||||||
|
- Old shopify order syncing date ([#23990](https://github.com/frappe/erpnext/pull/23990))
|
||||||
|
- Shipping chanrges not sync in erpnext from shopify ([#24114](https://github.com/frappe/erpnext/pull/24114))
|
||||||
|
- GSTR B2C report ([#24039](https://github.com/frappe/erpnext/pull/24039))
|
||||||
|
- Ignore cancelled entries in stock balance report ([#23757](https://github.com/frappe/erpnext/pull/23757))
|
||||||
|
- Stock ageing report not working ([#23923](https://github.com/frappe/erpnext/pull/23923))
|
||||||
|
- Incorrect assign to in Maintenance Schedule ([#23831](https://github.com/frappe/erpnext/pull/23831))
|
||||||
|
- Improve UX of DATEV report ([#23892](https://github.com/frappe/erpnext/pull/23892))
|
||||||
|
- Set SLA variance in seconds for Duration fieldtype ([#23765](https://github.com/frappe/erpnext/pull/23765))
|
||||||
|
- dDouble exception in payroll ([#24078](https://github.com/frappe/erpnext/pull/24078))
|
||||||
|
- Make asset dashboard charts public ([#23751](https://github.com/frappe/erpnext/pull/23751))
|
||||||
|
- Don't copy terms and discount from SO to PO ([#23903](https://github.com/frappe/erpnext/pull/23903))
|
||||||
|
- Ignore doctypes on company transaction delete ([#23864](https://github.com/frappe/erpnext/pull/23864))
|
||||||
|
- Error handling in Upload Attendance ([#23907](https://github.com/frappe/erpnext/pull/23907))
|
||||||
|
- Tax template update on customer address change ([#24160](https://github.com/frappe/erpnext/pull/24160))
|
||||||
|
- Not able to save bom ([#23910](https://github.com/frappe/erpnext/pull/23910))
|
||||||
|
- Enable Allow Auto Repeat for standard doctypes having auto_repeat field ([#23776](https://github.com/frappe/erpnext/pull/23776))
|
||||||
|
- Place of Supply fix in Sales Invoices ([#23785](https://github.com/frappe/erpnext/pull/23785))
|
||||||
|
- Opening invoices in GSTR-1 report ([#24117](https://github.com/frappe/erpnext/pull/24117))
|
||||||
|
- Partial serial no return issue ([#24208](https://github.com/frappe/erpnext/pull/24208))
|
||||||
|
- Import taxjar globally in the taxjar_integration module ([#24027](https://github.com/frappe/erpnext/pull/24027))
|
||||||
|
- Payroll attendance error ([#23887](https://github.com/frappe/erpnext/pull/23887))
|
||||||
|
- Loan application link on creating loan ([#23937](https://github.com/frappe/erpnext/pull/23937))
|
||||||
|
- POS item search includes non stock items ([#23914](https://github.com/frappe/erpnext/pull/23914))
|
||||||
|
- Paid amount in Sales Invoice POS return resets to 0 ([#24057](https://github.com/frappe/erpnext/pull/24057))
|
||||||
|
- Fiscal year can be shorter than 12 months ([#23838](https://github.com/frappe/erpnext/pull/23838))
|
||||||
|
- Loan repayment type option remove ([#23582](https://github.com/frappe/erpnext/pull/23582))
|
||||||
|
- Item wise tax calculation ([#23744](https://github.com/frappe/erpnext/pull/23744))
|
||||||
|
- Enabling track changes for stock settings ([#23982](https://github.com/frappe/erpnext/pull/23982))
|
||||||
|
- Added link of bank reconciliation and clearance in accounting desk page ([#23809](https://github.com/frappe/erpnext/pull/23809))
|
||||||
|
- Location data on Asset to use command(make_demo) ([#23825](https://github.com/frappe/erpnext/pull/23825))
|
||||||
|
- Handle Account and Item None not found in Opening Invoice Creation Tool ([#23559](https://github.com/frappe/erpnext/pull/23559))
|
||||||
|
- Multiple subcontracting issues ([#23662](https://github.com/frappe/erpnext/pull/23662))
|
||||||
|
- Sequence id override with workstation column ([#23810](https://github.com/frappe/erpnext/pull/23810))
|
||||||
|
- Leave policy dashboard fix and roles ([#24170](https://github.com/frappe/erpnext/pull/24170))
|
||||||
|
- Scan barcode does not update barcode item field in sales order ([#24090](https://github.com/frappe/erpnext/pull/24090))
|
||||||
|
- Item price duplicate checking ([#23408](https://github.com/frappe/erpnext/pull/23408))
|
||||||
|
- Tax template update on supplier change for India ([#24060](https://github.com/frappe/erpnext/pull/24060))
|
||||||
|
- Consumed qty logic for subcontracted raw materials ([#23314](https://github.com/frappe/erpnext/pull/23314))
|
||||||
|
- Finance book not getting added in journal Entry of asset value adjustment ([#24100](https://github.com/frappe/erpnext/pull/24100))
|
||||||
|
- Set proper state code in ewaybill JSON when GST category is SEZ ([#23953](https://github.com/frappe/erpnext/pull/23953))
|
||||||
|
- Copying po no when mapping doc ([#23729](https://github.com/frappe/erpnext/pull/23729))
|
||||||
|
- Duplicate items validation for POS Invoice when allow multiple items is disabled ([#23896](https://github.com/frappe/erpnext/pull/23896))
|
||||||
|
- Do not allow Company as accounting dimension ([#23749](https://github.com/frappe/erpnext/pull/23749))
|
||||||
|
- Validation for duplicate Tax Category ([#23978](https://github.com/frappe/erpnext/pull/23978))
|
||||||
|
- Therapy plan and session fixes ([#23817](https://github.com/frappe/erpnext/pull/23817))
|
||||||
|
- Pricing rule with transaction not working for additional product ([#24053](https://github.com/frappe/erpnext/pull/24053))
|
||||||
|
- Inpatient Medication Order and Entry fixes ([#23799](https://github.com/frappe/erpnext/pull/23799))
|
||||||
|
- Avoid using SQL query to get fiscal year dates ([#24050](https://github.com/frappe/erpnext/pull/24050))
|
||||||
|
- Auto Statewise gst tax template ([#23832](https://github.com/frappe/erpnext/pull/23832))
|
||||||
|
- On save sequence id column override with workstation ([#23812](https://github.com/frappe/erpnext/pull/23812))
|
||||||
|
- Multiple pricing rules are not working on selling side ([#22711](https://github.com/frappe/erpnext/pull/22711))
|
||||||
|
- Salary slip popup error ([#24192](https://github.com/frappe/erpnext/pull/24192))
|
||||||
|
- Multiple pricing rule with margin type as Percentage is not working ([#24204](https://github.com/frappe/erpnext/pull/24204))
|
||||||
|
- Allow statistical component in salary structure. ([#24424](https://github.com/frappe/erpnext/pull/24424))
|
||||||
|
- Set current asset value before calculating difference amount ([#24119](https://github.com/frappe/erpnext/pull/24119))
|
||||||
|
- To use Stock UoM in BOM Stock Report ([#24339](https://github.com/frappe/erpnext/pull/24339))
|
||||||
|
- Accounting entries of asset when submitting purchase receipt ([#24191](https://github.com/frappe/erpnext/pull/24191))
|
||||||
|
- Batch/Serial Selector for Scanned Batched Item ([#24338](https://github.com/frappe/erpnext/pull/24338))
|
||||||
|
- Link timesheets with corresponding projects ([#24346](https://github.com/frappe/erpnext/pull/24346))
|
||||||
|
- Material request wrong status issue ([#24019](https://github.com/frappe/erpnext/pull/24019))
|
||||||
|
- UX issues in e-invoicing ([#24358](https://github.com/frappe/erpnext/pull/24358))
|
||||||
|
- Company Wise Valuation Rate for RM in BOM ([#24324](https://github.com/frappe/erpnext/pull/24324))
|
||||||
|
- Stock ageing should not take cancelled stock entries. ([#24437](https://github.com/frappe/erpnext/pull/24437))
|
||||||
|
- Partial loan security unpledging ([#24252](https://github.com/frappe/erpnext/pull/24252))
|
||||||
|
- Asset depreciation ledger ([#24226](https://github.com/frappe/erpnext/pull/24226))
|
||||||
|
- Back Update from QC based on Batch No ([#24329](https://github.com/frappe/erpnext/pull/24329))
|
||||||
|
- Fix for not having fiscal year while creating new company ([#24130](https://github.com/frappe/erpnext/pull/24130))
|
||||||
|
- E-invoice print format not showing other charges ([#24474](https://github.com/frappe/erpnext/pull/24474))
|
||||||
|
- Tax template update on customer address change ([#24146](https://github.com/frappe/erpnext/pull/24146))
|
||||||
|
- Do not manufacture same serial no multiple times ([#24164](https://github.com/frappe/erpnext/pull/24164))
|
||||||
|
- Ignore group cost center validation for period closing voucher ([#24375](https://github.com/frappe/erpnext/pull/24375))
|
||||||
|
- Partial serial no return issue ([#24207](https://github.com/frappe/erpnext/pull/24207))
|
||||||
|
- GSTR-1 double entry issue ([#24376](https://github.com/frappe/erpnext/pull/24376))
|
||||||
|
- Not able to create dunning from sales invoice ([#24349](https://github.com/frappe/erpnext/pull/24349))
|
||||||
|
- Set company in leave allocation and leave ledger entry ([#24296](https://github.com/frappe/erpnext/pull/24296))
|
||||||
|
- Allow leave policy assignment to be canceled. ([#24265](https://github.com/frappe/erpnext/pull/24265))
|
||||||
|
- Removed all day event from shift assignment calendar ([#24397](https://github.com/frappe/erpnext/pull/24397))
|
||||||
|
- Tax calculation on salary slip for the first month ([#24272](https://github.com/frappe/erpnext/pull/24272))
|
||||||
|
- Validate tax template for tax category ([#24402](https://github.com/frappe/erpnext/pull/24402))
|
||||||
|
- Numeric/Non-numeric QI UX ([#24517](https://github.com/frappe/erpnext/pull/24517))
|
||||||
|
- Finished good produced qty validation ([#24220](https://github.com/frappe/erpnext/pull/24220))
|
||||||
|
- Incorrect serial no in the subcontracted purchase receipt ([#24354](https://github.com/frappe/erpnext/pull/24354))
|
||||||
|
- Don't validate warehouse values between Material Request and Stock Entry ([#24294](https://github.com/frappe/erpnext/pull/24294))
|
||||||
|
- Don't cancel job card if manufacturing entry has made ([#24063](https://github.com/frappe/erpnext/pull/24063))
|
||||||
|
- Subscription prepaid date validation ([#24356](https://github.com/frappe/erpnext/pull/24356))
|
||||||
|
- Payment Period based on invoice date report fix/refactor ([#24378](https://github.com/frappe/erpnext/pull/24378))
|
||||||
|
- Drop ship partial order fixed ([#24072](https://github.com/frappe/erpnext/pull/24072))
|
||||||
|
- Payment entry multi-currency issue ([#24332](https://github.com/frappe/erpnext/pull/24332))
|
||||||
|
- Multiple pricing rule issue ([#24515](https://github.com/frappe/erpnext/pull/24515))
|
||||||
|
- Last purchase rate not updating when voucher cancelled if only one voucher is present ([#24322](https://github.com/frappe/erpnext/pull/24322))
|
||||||
|
- Do not cancel reference document on Quality Inspection cancellation ([#24197](https://github.com/frappe/erpnext/pull/24197))
|
||||||
|
- Refactored fetching & validating address from erpnext rather than gst portal ([#24297](https://github.com/frappe/erpnext/pull/24297))
|
||||||
|
- Opportunity Status fix ([#22944](https://github.com/frappe/erpnext/pull/22944))
|
||||||
|
- Fixed stock and account balance syncing ([#24644](https://github.com/frappe/erpnext/pull/24644))
|
||||||
|
- Fixed incorrect stock ledger qty in the stock ledger report and bin ([#24649](https://github.com/frappe/erpnext/pull/24649))
|
||||||
|
- Fixed Consolidated Financial Statement report ([#24580](https://github.com/frappe/erpnext/pull/24580))
|
||||||
|
- Repost incompleted backdated transactions ([#24991](https://github.com/frappe/erpnext/pull/24991))
|
||||||
|
- Unequal debit and credit issue on RCM Invoice ([#24838](https://github.com/frappe/erpnext/pull/24838))
|
||||||
|
- Period list for exponential smoothing forecasting report ([#24983](https://github.com/frappe/erpnext/pull/24983))
|
||||||
|
- POS Opening Entry with empty balance detail rows ([#24891](https://github.com/frappe/erpnext/pull/24891))
|
||||||
|
- Use account_name only in consolidated report ([#24840](https://github.com/frappe/erpnext/pull/24840))
|
||||||
|
- Validation of job card in stock entry ([#24882](https://github.com/frappe/erpnext/pull/24882))
|
||||||
|
- Incorrect Nil Exempt and Non GST amount in GSTR3B report ([#24918](https://github.com/frappe/erpnext/pull/24918))
|
||||||
|
- TDS check getting checked after reload ([#24973](https://github.com/frappe/erpnext/pull/24973))
|
||||||
|
- Membership and Donation API fixes ([#24900](https://github.com/frappe/erpnext/pull/24900))
|
||||||
|
- Allow zero valuation in stock reconciliation ([#24985](https://github.com/frappe/erpnext/pull/24985))
|
||||||
|
- Simplified logic for additional salary ([#24907](https://github.com/frappe/erpnext/pull/24907))
|
||||||
|
- Allow to select item code in batch naming ([#24825](https://github.com/frappe/erpnext/pull/24825))
|
||||||
|
- Membership renewal validation (#24963) ([#24964](https://github.com/frappe/erpnext/pull/24964))
|
||||||
|
</details>
|
@ -659,6 +659,7 @@ class AccountsController(TransactionBase):
|
|||||||
'dr_or_cr': dr_or_cr,
|
'dr_or_cr': dr_or_cr,
|
||||||
'unadjusted_amount': flt(d.advance_amount),
|
'unadjusted_amount': flt(d.advance_amount),
|
||||||
'allocated_amount': flt(d.allocated_amount),
|
'allocated_amount': flt(d.allocated_amount),
|
||||||
|
'precision': d.precision('advance_amount'),
|
||||||
'exchange_rate': (self.conversion_rate
|
'exchange_rate': (self.conversion_rate
|
||||||
if self.party_account_currency != self.company_currency else 1),
|
if self.party_account_currency != self.company_currency else 1),
|
||||||
'grand_total': (self.base_grand_total
|
'grand_total': (self.base_grand_total
|
||||||
|
@ -6,6 +6,7 @@ import frappe
|
|||||||
from frappe import _, msgprint
|
from frappe import _, msgprint
|
||||||
from frappe.utils import flt,cint, cstr, getdate
|
from frappe.utils import flt,cint, cstr, getdate
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
from collections import OrderedDict
|
||||||
from erpnext.accounts.party import get_party_details
|
from erpnext.accounts.party import get_party_details
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
|
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
|
||||||
@ -391,10 +392,12 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
|
batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
|
||||||
qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)
|
qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)
|
||||||
|
|
||||||
for batch_data in batches_qty:
|
for batch_data in batches_qty:
|
||||||
qty = batch_data['qty']
|
qty = batch_data['qty']
|
||||||
raw_material.batch_no = batch_data['batch']
|
raw_material.batch_no = batch_data['batch']
|
||||||
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
if qty > 0:
|
||||||
|
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
||||||
else:
|
else:
|
||||||
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
||||||
|
|
||||||
@ -1056,7 +1059,7 @@ def get_transferred_batch_qty_map(purchase_order, fg_item):
|
|||||||
for batch_data in transferred_batches:
|
for batch_data in transferred_batches:
|
||||||
key = ((batch_data.item_code, fg_item)
|
key = ((batch_data.item_code, fg_item)
|
||||||
if batch_data.subcontracted_item else (batch_data.item_code, purchase_order))
|
if batch_data.subcontracted_item else (batch_data.item_code, purchase_order))
|
||||||
transferred_batch_qty_map.setdefault(key, {})
|
transferred_batch_qty_map.setdefault(key, OrderedDict())
|
||||||
transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty
|
transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty
|
||||||
|
|
||||||
return transferred_batch_qty_map
|
return transferred_batch_qty_map
|
||||||
@ -1109,8 +1112,14 @@ def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty
|
|||||||
if available_qty >= required_qty:
|
if available_qty >= required_qty:
|
||||||
available_batches.append({'batch': batch, 'qty': required_qty})
|
available_batches.append({'batch': batch, 'qty': required_qty})
|
||||||
break
|
break
|
||||||
else:
|
elif available_qty != 0:
|
||||||
available_batches.append({'batch': batch, 'qty': available_qty})
|
available_batches.append({'batch': batch, 'qty': available_qty})
|
||||||
required_qty -= available_qty
|
required_qty -= available_qty
|
||||||
|
|
||||||
|
for row in available_batches:
|
||||||
|
if backflushed_batches.get(row.get('batch'), 0) > 0:
|
||||||
|
backflushed_batches[row.get('batch')] += row.get('qty')
|
||||||
|
else:
|
||||||
|
backflushed_batches[row.get('batch')] = row.get('qty')
|
||||||
|
|
||||||
return available_batches
|
return available_batches
|
||||||
|
@ -325,7 +325,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
|||||||
and status not in ("Stopped", "Closed") %(fcond)s
|
and status not in ("Stopped", "Closed") %(fcond)s
|
||||||
and (
|
and (
|
||||||
(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
|
(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
|
||||||
or `tabDelivery Note`.grand_total = 0
|
or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
|
||||||
or (
|
or (
|
||||||
`tabDelivery Note`.is_return = 1
|
`tabDelivery Note`.is_return = 1
|
||||||
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)
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
|
from erpnext.stock.utils import get_incoming_rate
|
||||||
from frappe.utils import flt, get_datetime, format_datetime
|
from frappe.utils import flt, get_datetime, format_datetime
|
||||||
|
|
||||||
class StockOverReturnError(frappe.ValidationError): pass
|
class StockOverReturnError(frappe.ValidationError): pass
|
||||||
@ -389,10 +390,24 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def get_rate_for_return(voucher_type, voucher_no, item_code, return_against=None, item_row=None, voucher_detail_no=None):
|
def get_rate_for_return(voucher_type, voucher_no, item_code, return_against=None,
|
||||||
|
item_row=None, voucher_detail_no=None, sle=None):
|
||||||
if not return_against:
|
if not return_against:
|
||||||
return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
|
return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
|
||||||
|
|
||||||
|
if not return_against and voucher_type == 'Sales Invoice' and sle:
|
||||||
|
return get_incoming_rate({
|
||||||
|
"item_code": sle.item_code,
|
||||||
|
"warehouse": sle.warehouse,
|
||||||
|
"posting_date": sle.get('posting_date'),
|
||||||
|
"posting_time": sle.get('posting_time'),
|
||||||
|
"qty": sle.actual_qty,
|
||||||
|
"serial_no": sle.get('serial_no'),
|
||||||
|
"company": sle.company,
|
||||||
|
"voucher_type": sle.voucher_type,
|
||||||
|
"voucher_no": sle.voucher_no
|
||||||
|
}, raise_error_if_no_rate=False)
|
||||||
|
|
||||||
return_against_item_field = get_return_against_item_fields(voucher_type)
|
return_against_item_field = get_return_against_item_fields(voucher_type)
|
||||||
|
|
||||||
filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
|
filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
|
||||||
|
@ -311,14 +311,16 @@ class SellingController(StockController):
|
|||||||
|
|
||||||
items = self.get("items") + (self.get("packed_items") or [])
|
items = self.get("items") + (self.get("packed_items") or [])
|
||||||
for d in items:
|
for d in items:
|
||||||
if not cint(self.get("is_return")):
|
if not self.get("return_against"):
|
||||||
# Get incoming rate based on original item cost based on valuation method
|
# Get incoming rate based on original item cost based on valuation method
|
||||||
|
qty = flt(d.get('stock_qty') or d.get('actual_qty'))
|
||||||
|
|
||||||
d.incoming_rate = get_incoming_rate({
|
d.incoming_rate = get_incoming_rate({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
"posting_date": self.get('posting_date') or self.get('transaction_date'),
|
"posting_date": self.get('posting_date') or self.get('transaction_date'),
|
||||||
"posting_time": self.get('posting_time') or nowtime(),
|
"posting_time": self.get('posting_time') or nowtime(),
|
||||||
"qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
|
"qty": qty if cint(self.get("is_return")) else (-1 * qty),
|
||||||
"serial_no": d.get('serial_no'),
|
"serial_no": d.get('serial_no'),
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"voucher_type": self.doctype,
|
"voucher_type": self.doctype,
|
||||||
|
@ -409,8 +409,7 @@ class StockController(AccountsController):
|
|||||||
def set_rate_of_stock_uom(self):
|
def set_rate_of_stock_uom(self):
|
||||||
if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
|
if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.conversion_factor:
|
d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
|
||||||
d.stock_uom_rate = d.rate / d.conversion_factor
|
|
||||||
|
|
||||||
def validate_internal_transfer(self):
|
def validate_internal_transfer(self):
|
||||||
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
|
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
|
||||||
|
@ -113,10 +113,12 @@ class calculate_taxes_and_totals(object):
|
|||||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||||
if flt(item.rate_with_margin) > 0:
|
if flt(item.rate_with_margin) > 0:
|
||||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||||
|
|
||||||
if item.discount_amount and not item.discount_percentage:
|
if item.discount_amount and not item.discount_percentage:
|
||||||
item.rate -= item.discount_amount
|
item.rate = item.rate_with_margin - item.discount_amount
|
||||||
else:
|
else:
|
||||||
item.discount_amount = item.rate_with_margin - item.rate
|
item.discount_amount = item.rate_with_margin - item.rate
|
||||||
|
|
||||||
elif flt(item.price_list_rate) > 0:
|
elif flt(item.price_list_rate) > 0:
|
||||||
item.discount_amount = item.price_list_rate - item.rate
|
item.discount_amount = item.price_list_rate - item.rate
|
||||||
elif flt(item.price_list_rate) > 0 and not item.discount_amount:
|
elif flt(item.price_list_rate) > 0 and not item.discount_amount:
|
||||||
@ -147,7 +149,9 @@ class calculate_taxes_and_totals(object):
|
|||||||
validate_taxes_and_charges(tax)
|
validate_taxes_and_charges(tax)
|
||||||
validate_inclusive_tax(tax, self.doc)
|
validate_inclusive_tax(tax, self.doc)
|
||||||
|
|
||||||
tax.item_wise_tax_detail = {}
|
if not self.doc.get('is_consolidated'):
|
||||||
|
tax.item_wise_tax_detail = {}
|
||||||
|
|
||||||
tax_fields = ["total", "tax_amount_after_discount_amount",
|
tax_fields = ["total", "tax_amount_after_discount_amount",
|
||||||
"tax_amount_for_current_item", "grand_total_for_current_item",
|
"tax_amount_for_current_item", "grand_total_for_current_item",
|
||||||
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
|
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
|
||||||
@ -287,10 +291,13 @@ class calculate_taxes_and_totals(object):
|
|||||||
# set precision in the last item iteration
|
# set precision in the last item iteration
|
||||||
if n == len(self.doc.get("items")) - 1:
|
if n == len(self.doc.get("items")) - 1:
|
||||||
self.round_off_totals(tax)
|
self.round_off_totals(tax)
|
||||||
|
self._set_in_company_currency(tax,
|
||||||
|
["tax_amount", "tax_amount_after_discount_amount"])
|
||||||
|
|
||||||
|
self.round_off_base_values(tax)
|
||||||
self.set_cumulative_total(i, tax)
|
self.set_cumulative_total(i, tax)
|
||||||
|
|
||||||
self._set_in_company_currency(tax,
|
self._set_in_company_currency(tax, ["total"])
|
||||||
["total", "tax_amount", "tax_amount_after_discount_amount"])
|
|
||||||
|
|
||||||
# adjust Discount Amount loss in last tax iteration
|
# adjust Discount Amount loss in last tax iteration
|
||||||
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
|
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
|
||||||
@ -337,18 +344,11 @@ class calculate_taxes_and_totals(object):
|
|||||||
elif tax.charge_type == "On Item Quantity":
|
elif tax.charge_type == "On Item Quantity":
|
||||||
current_tax_amount = tax_rate * item.qty
|
current_tax_amount = tax_rate * item.qty
|
||||||
|
|
||||||
current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
|
if not self.doc.get("is_consolidated"):
|
||||||
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
|
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
|
||||||
|
|
||||||
return current_tax_amount
|
return current_tax_amount
|
||||||
|
|
||||||
def get_final_current_tax_amount(self, tax, current_tax_amount):
|
|
||||||
# Some countries need individual tax components to be rounded
|
|
||||||
# Handeled via regional doctypess
|
|
||||||
if tax.account_head in frappe.flags.round_off_applicable_accounts:
|
|
||||||
current_tax_amount = round(current_tax_amount, 0)
|
|
||||||
return current_tax_amount
|
|
||||||
|
|
||||||
def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
|
def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
|
||||||
# store tax breakup for each item
|
# store tax breakup for each item
|
||||||
key = item.item_code or item.item_name
|
key = item.item_code or item.item_name
|
||||||
@ -359,10 +359,20 @@ class calculate_taxes_and_totals(object):
|
|||||||
tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
|
tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
|
||||||
|
|
||||||
def round_off_totals(self, tax):
|
def round_off_totals(self, tax):
|
||||||
|
if tax.account_head in frappe.flags.round_off_applicable_accounts:
|
||||||
|
tax.tax_amount = round(tax.tax_amount, 0)
|
||||||
|
tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
|
||||||
|
|
||||||
tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
|
tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
|
||||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
|
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
|
||||||
tax.precision("tax_amount"))
|
tax.precision("tax_amount"))
|
||||||
|
|
||||||
|
def round_off_base_values(self, tax):
|
||||||
|
# Round off to nearest integer based on regional settings
|
||||||
|
if tax.account_head in frappe.flags.round_off_applicable_accounts:
|
||||||
|
tax.base_tax_amount = round(tax.base_tax_amount, 0)
|
||||||
|
tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
|
||||||
|
|
||||||
def manipulate_grand_total_for_inclusive_tax(self):
|
def manipulate_grand_total_for_inclusive_tax(self):
|
||||||
# if fully inclusive taxes and diff
|
# if fully inclusive taxes and diff
|
||||||
if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]):
|
if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]):
|
||||||
@ -440,8 +450,9 @@ class calculate_taxes_and_totals(object):
|
|||||||
self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
|
self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
for tax in self.doc.get("taxes"):
|
if not self.doc.get('is_consolidated'):
|
||||||
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
|
for tax in self.doc.get("taxes"):
|
||||||
|
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
|
||||||
|
|
||||||
def set_discount_amount(self):
|
def set_discount_amount(self):
|
||||||
if self.doc.additional_discount_percentage:
|
if self.doc.additional_discount_percentage:
|
||||||
|
@ -25,7 +25,7 @@ def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_p
|
|||||||
|
|
||||||
if not filters: filters = []
|
if not filters: filters = []
|
||||||
|
|
||||||
if doctype in ['Supplier Quotation', 'Purchase Invoice', 'Quotation']:
|
if doctype in ['Supplier Quotation', 'Purchase Invoice']:
|
||||||
filters.append((doctype, 'docstatus', '<', 2))
|
filters.append((doctype, 'docstatus', '<', 2))
|
||||||
else:
|
else:
|
||||||
filters.append((doctype, 'docstatus', '=', 1))
|
filters.append((doctype, 'docstatus', '=', 1))
|
||||||
|
8
erpnext/crm/doctype/lead_source/lead_source.js
Normal file
8
erpnext/crm/doctype/lead_source/lead_source.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Lead Source', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
62
erpnext/crm/doctype/lead_source/lead_source.json
Normal file
62
erpnext/crm/doctype/lead_source/lead_source.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "field:source_name",
|
||||||
|
"creation": "2016-09-16 01:47:47.382372",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"source_name",
|
||||||
|
"details"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "source_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Source Name",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "details",
|
||||||
|
"fieldtype": "Text Editor",
|
||||||
|
"label": "Details"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-02-08 12:51:48.971517",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Lead Source",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales User",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
class LeadSource(Document):
|
class LeadSource(Document):
|
@ -1,12 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Lead Source')
|
|
||||||
|
|
||||||
class TestLeadSource(unittest.TestCase):
|
class TestLeadSource(unittest.TestCase):
|
||||||
pass
|
pass
|
@ -22,7 +22,7 @@ class ShopifySettings(unittest.TestCase):
|
|||||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
|
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
|
||||||
|
|
||||||
# use the fixture data
|
# use the fixture data
|
||||||
import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
|
import_doc(path=frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
|
||||||
|
|
||||||
frappe.reload_doctype("Customer")
|
frappe.reload_doctype("Customer")
|
||||||
frappe.reload_doctype("Sales Order")
|
frappe.reload_doctype("Sales Order")
|
||||||
|
@ -39,11 +39,13 @@ frappe.ui.form.on('Patient Assessment', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_score_range: function(frm) {
|
set_score_range: function(frm) {
|
||||||
let options = [];
|
let options = [''];
|
||||||
for(let i = frm.doc.scale_min; i <= frm.doc.scale_max; i++) {
|
for(let i = frm.doc.scale_min; i <= frm.doc.scale_max; i++) {
|
||||||
options.push(i);
|
options.push(i);
|
||||||
}
|
}
|
||||||
frappe.meta.get_docfield('Patient Assessment Sheet', 'score', frm.doc.name).options = [''].concat(options);
|
frm.fields_dict.assessment_sheet.grid.update_docfield_property(
|
||||||
|
'score', 'options', options
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
calculate_total_score: function(frm, cdt, cdn) {
|
calculate_total_score: function(frm, cdt, cdn) {
|
||||||
@ -83,4 +85,4 @@ frappe.ui.form.on('Patient Assessment Sheet', {
|
|||||||
score: function(frm, cdt, cdn) {
|
score: function(frm, cdt, cdn) {
|
||||||
frm.events.calculate_total_score(frm, cdt, cdn);
|
frm.events.calculate_total_score(frm, cdt, cdn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -58,8 +58,12 @@ frappe.ui.form.on('Therapy Plan', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.therapy_plan_template) {
|
if (frm.doc.therapy_plan_template) {
|
||||||
frappe.meta.get_docfield('Therapy Plan Detail', 'therapy_type', frm.doc.name).read_only = 1;
|
frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
|
||||||
frappe.meta.get_docfield('Therapy Plan Detail', 'no_of_sessions', frm.doc.name).read_only = 1;
|
'therapy_type', 'read_only', 1
|
||||||
|
);
|
||||||
|
frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
|
||||||
|
'no_of_sessions', 'read_only', 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -126,4 +130,4 @@ frappe.ui.form.on('Therapy Plan Detail', {
|
|||||||
frm.set_value('total_sessions', total);
|
frm.set_value('total_sessions', total);
|
||||||
refresh_field('total_sessions');
|
refresh_field('total_sessions');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -260,7 +260,11 @@ doc_events = {
|
|||||||
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
||||||
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
|
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
|
||||||
],
|
],
|
||||||
"on_trash": "erpnext.regional.check_deletion_permission"
|
"on_trash": "erpnext.regional.check_deletion_permission",
|
||||||
|
"validate": [
|
||||||
|
"erpnext.regional.india.utils.validate_document_name",
|
||||||
|
"erpnext.regional.india.utils.update_taxable_values"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"Purchase Invoice": {
|
"Purchase Invoice": {
|
||||||
"validate": [
|
"validate": [
|
||||||
@ -282,9 +286,6 @@ doc_events = {
|
|||||||
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
|
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
|
||||||
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
|
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
|
||||||
},
|
},
|
||||||
('Sales Invoice', 'Purchase Invoice'): {
|
|
||||||
'validate': ['erpnext.regional.india.utils.validate_document_name']
|
|
||||||
},
|
|
||||||
"Contact": {
|
"Contact": {
|
||||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||||
"after_insert": "erpnext.telephony.doctype.call_log.call_log.link_existing_conversations",
|
"after_insert": "erpnext.telephony.doctype.call_log.call_log.link_existing_conversations",
|
||||||
@ -306,6 +307,8 @@ auto_cancel_exempted_doctypes= [
|
|||||||
"Inpatient Medication Entry"
|
"Inpatient Medication Entry"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
after_migrate = ["erpnext.setup.install.update_select_perm_after_install"]
|
||||||
|
|
||||||
scheduler_events = {
|
scheduler_events = {
|
||||||
"cron": {
|
"cron": {
|
||||||
"0/30 * * * *": [
|
"0/30 * * * *": [
|
||||||
|
@ -35,7 +35,8 @@ class Attendance(Document):
|
|||||||
and docstatus != 2
|
and docstatus != 2
|
||||||
""", (self.employee, getdate(self.attendance_date), self.name))
|
""", (self.employee, getdate(self.attendance_date), self.name))
|
||||||
if res:
|
if res:
|
||||||
frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
|
frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
|
||||||
|
frappe.bold(self.employee), frappe.bold(self.attendance_date)))
|
||||||
|
|
||||||
def check_leave_record(self):
|
def check_leave_record(self):
|
||||||
leave_record = frappe.db.sql("""
|
leave_record = frappe.db.sql("""
|
||||||
|
@ -200,7 +200,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:42:47.321368",
|
"modified": "2021-03-31 22:31:53.746659",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Advance",
|
"name": "Employee Advance",
|
||||||
|
@ -154,7 +154,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:45:27.948207",
|
"modified": "2021-03-31 22:32:55.492327",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Encashment",
|
"name": "Leave Encashment",
|
||||||
|
@ -187,4 +187,4 @@ def get_job_offer(ja_list):
|
|||||||
else:
|
else:
|
||||||
ja_joff_map[offer.job_applicant].append(offer)
|
ja_joff_map[offer.job_applicant].append(offer)
|
||||||
|
|
||||||
return ja_joff_map
|
return ja_joff_map
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"hide_custom": 0,
|
"hide_custom": 0,
|
||||||
"icon": "hr",
|
"icon": "hr",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
"is_default": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "HR",
|
"label": "HR",
|
||||||
"links": [
|
"links": [
|
||||||
@ -226,42 +227,12 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Leave Application",
|
|
||||||
"link_to": "Leave Application",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Leave Allocation",
|
|
||||||
"link_to": "Leave Allocation",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Leave Type",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Leave Policy",
|
|
||||||
"link_to": "Leave Policy",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Leave Period",
|
"label": "Holiday List",
|
||||||
"link_to": "Leave Period",
|
"link_to": "Holiday List",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -280,8 +251,28 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Holiday List",
|
"label": "Leave Period",
|
||||||
"link_to": "Holiday List",
|
"link_to": "Leave Period",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Leave Type",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Leave Policy",
|
||||||
|
"link_to": "Leave Policy",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Leave Policy",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Leave Policy Assignment",
|
||||||
|
"link_to": "Leave Policy Assignment",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -290,8 +281,18 @@
|
|||||||
"dependencies": "Employee",
|
"dependencies": "Employee",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Compensatory Leave Request",
|
"label": "Leave Application",
|
||||||
"link_to": "Compensatory Leave Request",
|
"link_to": "Leave Application",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Employee",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Leave Allocation",
|
||||||
|
"link_to": "Leave Allocation",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -317,12 +318,12 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "Leave Application",
|
"dependencies": "Employee",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 1,
|
"is_query_report": 0,
|
||||||
"label": "Employee Leave Balance",
|
"label": "Compensatory Leave Request",
|
||||||
"link_to": "Employee Leave Balance",
|
"link_to": "Compensatory Leave Request",
|
||||||
"link_type": "Report",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
@ -383,16 +384,6 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"dependencies": "Attendance",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 1,
|
|
||||||
"label": "Monthly Attendance Sheet",
|
|
||||||
"link_to": "Monthly Attendance Sheet",
|
|
||||||
"link_type": "Report",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -420,6 +411,15 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Travel Request",
|
||||||
|
"link_to": "Travel Request",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -464,6 +464,15 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Driver",
|
||||||
|
"link_to": "Driver",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -541,6 +550,24 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Appointment Letter",
|
||||||
|
"link_to": "Appointment Letter",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Appointment Letter Template",
|
||||||
|
"link_to": "Appointment Letter Template",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -625,33 +652,6 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Reports",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Card Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 1,
|
|
||||||
"label": "Employee Birthday",
|
|
||||||
"link_to": "Employee Birthday",
|
|
||||||
"link_type": "Report",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 1,
|
|
||||||
"label": "Employees working on a holiday",
|
|
||||||
"link_to": "Employees working on a holiday",
|
|
||||||
"link_type": "Report",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -702,7 +702,74 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Employee Tax and Benefits",
|
"label": "Key Reports",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Card Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Attendance",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Monthly Attendance Sheet",
|
||||||
|
"link_to": "Monthly Attendance Sheet",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Staffing Plan",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Recruitment Analytics",
|
||||||
|
"link_to": "Recruitment Analytics",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Employee",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Employee Analytics",
|
||||||
|
"link_to": "Employee Analytics",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Employee",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Employee Leave Balance",
|
||||||
|
"link_to": "Employee Leave Balance",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Employee",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Employee Leave Balance Summary",
|
||||||
|
"link_to": "Employee Leave Balance Summary",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "Employee Advance",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "Employee Advance Summary",
|
||||||
|
"link_to": "Employee Advance Summary",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Other Reports",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
@ -710,74 +777,44 @@
|
|||||||
"dependencies": "Employee",
|
"dependencies": "Employee",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Employee Tax Exemption Declaration",
|
"label": "Employee Information",
|
||||||
"link_to": "Employee Tax Exemption Declaration",
|
"link_to": "Employee Information",
|
||||||
"link_type": "DocType",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "Employee",
|
"dependencies": "Employee",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 1,
|
||||||
"label": "Employee Tax Exemption Proof Submission",
|
"label": "Employee Birthday",
|
||||||
"link_to": "Employee Tax Exemption Proof Submission",
|
"link_to": "Employee Birthday",
|
||||||
"link_type": "DocType",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee, Payroll Period",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Employee Other Income",
|
|
||||||
"link_to": "Employee Other Income",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "Employee",
|
"dependencies": "Employee",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 1,
|
||||||
"label": "Employee Benefit Application",
|
"label": "Employees Working on a Holiday",
|
||||||
"link_to": "Employee Benefit Application",
|
"link_to": "Employees working on a holiday",
|
||||||
"link_type": "DocType",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "Employee",
|
"dependencies": "Daily Work Summary",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 1,
|
||||||
"label": "Employee Benefit Claim",
|
"label": "Daily Work Summary Replies",
|
||||||
"link_to": "Employee Benefit Claim",
|
"link_to": "Daily Work Summary Replies",
|
||||||
"link_type": "DocType",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Employee Tax Exemption Category",
|
|
||||||
"link_to": "Employee Tax Exemption Category",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "Employee",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Employee Tax Exemption Sub Category",
|
|
||||||
"link_to": "Employee Tax Exemption Sub Category",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-01-21 13:38:38.941001",
|
"modified": "2021-03-24 17:35:21.483297",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR",
|
"name": "HR",
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"rate_of_interest",
|
"rate_of_interest",
|
||||||
"is_secured_loan",
|
"is_secured_loan",
|
||||||
"disbursement_date",
|
"disbursement_date",
|
||||||
|
"closure_date",
|
||||||
"disbursed_amount",
|
"disbursed_amount",
|
||||||
"column_break_11",
|
"column_break_11",
|
||||||
"maximum_loan_amount",
|
"maximum_loan_amount",
|
||||||
@ -348,12 +349,18 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "closure_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Closure Date",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-24 12:27:23.208240",
|
"modified": "2021-04-10 09:28:21.946972",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan",
|
"name": "Loan",
|
||||||
|
@ -523,33 +523,7 @@ class TestLoan(unittest.TestCase):
|
|||||||
self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
|
self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
|
||||||
|
|
||||||
def test_penalty(self):
|
def test_penalty(self):
|
||||||
pledge = [{
|
loan, amounts = create_loan_scenario_for_penalty(self)
|
||||||
"loan_security": "Test Security 1",
|
|
||||||
"qty": 4000.00
|
|
||||||
}]
|
|
||||||
|
|
||||||
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
|
|
||||||
create_pledge(loan_application)
|
|
||||||
|
|
||||||
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
|
|
||||||
loan.submit()
|
|
||||||
|
|
||||||
self.assertEquals(loan.loan_amount, 1000000)
|
|
||||||
|
|
||||||
first_date = '2019-10-01'
|
|
||||||
last_date = '2019-10-30'
|
|
||||||
|
|
||||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
|
|
||||||
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
|
|
||||||
|
|
||||||
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
|
|
||||||
paid_amount = amounts['interest_amount']/2
|
|
||||||
|
|
||||||
repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
|
|
||||||
paid_amount)
|
|
||||||
|
|
||||||
repayment_entry.submit()
|
|
||||||
|
|
||||||
# 30 days - grace period
|
# 30 days - grace period
|
||||||
penalty_days = 30 - 4
|
penalty_days = 30 - 4
|
||||||
penalty_applicable_amount = flt(amounts['interest_amount']/2)
|
penalty_applicable_amount = flt(amounts['interest_amount']/2)
|
||||||
@ -559,8 +533,28 @@ class TestLoan(unittest.TestCase):
|
|||||||
calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
|
calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
|
||||||
{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
|
{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
|
||||||
|
|
||||||
|
self.assertEquals(loan.loan_amount, 1000000)
|
||||||
self.assertEquals(calculated_penalty_amount, penalty_amount)
|
self.assertEquals(calculated_penalty_amount, penalty_amount)
|
||||||
|
|
||||||
|
def test_penalty_repayment(self):
|
||||||
|
loan, dummy = create_loan_scenario_for_penalty(self)
|
||||||
|
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:00')
|
||||||
|
|
||||||
|
first_penalty = 10000
|
||||||
|
second_penalty = amounts['penalty_amount'] - 10000
|
||||||
|
|
||||||
|
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:00', 10000)
|
||||||
|
repayment_entry.submit()
|
||||||
|
|
||||||
|
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
|
||||||
|
self.assertEquals(amounts['penalty_amount'], second_penalty)
|
||||||
|
|
||||||
|
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
|
||||||
|
repayment_entry.submit()
|
||||||
|
|
||||||
|
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02')
|
||||||
|
self.assertEquals(amounts['penalty_amount'], 0)
|
||||||
|
|
||||||
def test_loan_write_off_limit(self):
|
def test_loan_write_off_limit(self):
|
||||||
pledge = [{
|
pledge = [{
|
||||||
"loan_security": "Test Security 1",
|
"loan_security": "Test Security 1",
|
||||||
@ -651,6 +645,32 @@ class TestLoan(unittest.TestCase):
|
|||||||
amounts = calculate_amounts(loan.name, add_days(last_date, 5))
|
amounts = calculate_amounts(loan.name, add_days(last_date, 5))
|
||||||
self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
|
self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
|
||||||
|
|
||||||
|
def create_loan_scenario_for_penalty(doc):
|
||||||
|
pledge = [{
|
||||||
|
"loan_security": "Test Security 1",
|
||||||
|
"qty": 4000.00
|
||||||
|
}]
|
||||||
|
|
||||||
|
loan_application = create_loan_application('_Test Company', doc.applicant2, 'Demand Loan', pledge)
|
||||||
|
create_pledge(loan_application)
|
||||||
|
loan = create_demand_loan(doc.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
|
||||||
|
loan.submit()
|
||||||
|
|
||||||
|
first_date = '2019-10-01'
|
||||||
|
last_date = '2019-10-30'
|
||||||
|
|
||||||
|
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
|
||||||
|
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
|
||||||
|
|
||||||
|
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
|
||||||
|
paid_amount = amounts['interest_amount']/2
|
||||||
|
|
||||||
|
repayment_entry = create_repayment_entry(loan.name, doc.applicant2, add_days(last_date, 5),
|
||||||
|
paid_amount)
|
||||||
|
|
||||||
|
repayment_entry.submit()
|
||||||
|
|
||||||
|
return loan, amounts
|
||||||
|
|
||||||
def create_loan_accounts():
|
def create_loan_accounts():
|
||||||
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
|
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"customer_details_section",
|
"customer_details_section",
|
||||||
"bank_account",
|
"bank_account",
|
||||||
|
"disbursement_references_section",
|
||||||
|
"reference_date",
|
||||||
|
"column_break_17",
|
||||||
|
"reference_number",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -126,12 +130,31 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_8",
|
"fieldname": "column_break_8",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "disbursement_references_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Disbursement References"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Reference Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_17",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_number",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Reference Number"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-06 10:04:30.882322",
|
"modified": "2021-04-10 10:03:41.502210",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Disbursement",
|
"name": "Loan Disbursement",
|
||||||
|
@ -239,14 +239,16 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "total_penalty_paid",
|
"fieldname": "total_penalty_paid",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Total Penalty Paid",
|
"label": "Total Penalty Paid",
|
||||||
"options": "Company:company:default_currency"
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-05 13:45:19.137896",
|
"modified": "2021-04-10 10:00:31.859076",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Repayment",
|
"name": "Loan Repayment",
|
||||||
|
@ -21,6 +21,7 @@ class LoanRepayment(AccountsController):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
amounts = calculate_amounts(self.against_loan, self.posting_date)
|
amounts = calculate_amounts(self.against_loan, self.posting_date)
|
||||||
self.set_missing_values(amounts)
|
self.set_missing_values(amounts)
|
||||||
|
self.check_future_entries()
|
||||||
self.validate_amount()
|
self.validate_amount()
|
||||||
self.allocate_amounts(amounts)
|
self.allocate_amounts(amounts)
|
||||||
|
|
||||||
@ -69,16 +70,19 @@ class LoanRepayment(AccountsController):
|
|||||||
if amounts.get('due_date'):
|
if amounts.get('due_date'):
|
||||||
self.due_date = amounts.get('due_date')
|
self.due_date = amounts.get('due_date')
|
||||||
|
|
||||||
|
def check_future_entries(self):
|
||||||
|
future_repayment_date = frappe.db.get_value("Loan Repayment", {"posting_date": (">", self.posting_date),
|
||||||
|
"docstatus": 1, "against_loan": self.against_loan}, 'posting_date')
|
||||||
|
|
||||||
|
if future_repayment_date:
|
||||||
|
frappe.throw("Repayment already made till date {0}".format(get_datetime(future_repayment_date)))
|
||||||
|
|
||||||
def validate_amount(self):
|
def validate_amount(self):
|
||||||
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
||||||
|
|
||||||
if not self.amount_paid:
|
if not self.amount_paid:
|
||||||
frappe.throw(_("Amount paid cannot be zero"))
|
frappe.throw(_("Amount paid cannot be zero"))
|
||||||
|
|
||||||
if not self.shortfall_amount and self.amount_paid < self.penalty_amount:
|
|
||||||
msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
|
|
||||||
frappe.throw(msg)
|
|
||||||
|
|
||||||
def book_unaccrued_interest(self):
|
def book_unaccrued_interest(self):
|
||||||
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
||||||
if self.total_interest_paid > self.interest_payable:
|
if self.total_interest_paid > self.interest_payable:
|
||||||
@ -223,6 +227,14 @@ class LoanRepayment(AccountsController):
|
|||||||
gle_map = []
|
gle_map = []
|
||||||
loan_details = frappe.get_doc("Loan", self.against_loan)
|
loan_details = frappe.get_doc("Loan", self.against_loan)
|
||||||
|
|
||||||
|
if self.shortfall_amount and self.amount_paid > self.shortfall_amount:
|
||||||
|
remarks = _("Shortfall Repayment of {0}.\nRepayment against Loan: {1}").format(self.shortfall_amount,
|
||||||
|
self.against_loan)
|
||||||
|
elif self.shortfall_amount:
|
||||||
|
remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount)
|
||||||
|
else:
|
||||||
|
remarks = _("Repayment against Loan: ") + self.against_loan
|
||||||
|
|
||||||
if self.total_penalty_paid:
|
if self.total_penalty_paid:
|
||||||
gle_map.append(
|
gle_map.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@ -263,7 +275,7 @@ class LoanRepayment(AccountsController):
|
|||||||
"debit_in_account_currency": self.amount_paid,
|
"debit_in_account_currency": self.amount_paid,
|
||||||
"against_voucher_type": "Loan",
|
"against_voucher_type": "Loan",
|
||||||
"against_voucher": self.against_loan,
|
"against_voucher": self.against_loan,
|
||||||
"remarks": _("Repayment against Loan: ") + self.against_loan,
|
"remarks": remarks,
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
"posting_date": getdate(self.posting_date)
|
"posting_date": getdate(self.posting_date)
|
||||||
})
|
})
|
||||||
@ -279,7 +291,7 @@ class LoanRepayment(AccountsController):
|
|||||||
"credit_in_account_currency": self.amount_paid,
|
"credit_in_account_currency": self.amount_paid,
|
||||||
"against_voucher_type": "Loan",
|
"against_voucher_type": "Loan",
|
||||||
"against_voucher": self.against_loan,
|
"against_voucher": self.against_loan,
|
||||||
"remarks": _("Repayment against Loan: ") + self.against_loan,
|
"remarks": remarks,
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
"posting_date": getdate(self.posting_date)
|
"posting_date": getdate(self.posting_date)
|
||||||
})
|
})
|
||||||
@ -307,7 +319,9 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
|
|||||||
|
|
||||||
return lr
|
return lr
|
||||||
|
|
||||||
def get_accrued_interest_entries(against_loan):
|
def get_accrued_interest_entries(against_loan, posting_date=None):
|
||||||
|
if not posting_date:
|
||||||
|
posting_date = getdate()
|
||||||
|
|
||||||
unpaid_accrued_entries = frappe.db.sql(
|
unpaid_accrued_entries = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
@ -318,15 +332,28 @@ def get_accrued_interest_entries(against_loan):
|
|||||||
`tabLoan Interest Accrual`
|
`tabLoan Interest Accrual`
|
||||||
WHERE
|
WHERE
|
||||||
loan = %s
|
loan = %s
|
||||||
|
AND posting_date <= %s
|
||||||
AND (interest_amount - paid_interest_amount > 0 OR
|
AND (interest_amount - paid_interest_amount > 0 OR
|
||||||
payable_principal_amount - paid_principal_amount > 0)
|
payable_principal_amount - paid_principal_amount > 0)
|
||||||
AND
|
AND
|
||||||
docstatus = 1
|
docstatus = 1
|
||||||
ORDER BY posting_date
|
ORDER BY posting_date
|
||||||
""", (against_loan), as_dict=1)
|
""", (against_loan, posting_date), as_dict=1)
|
||||||
|
|
||||||
return unpaid_accrued_entries
|
return unpaid_accrued_entries
|
||||||
|
|
||||||
|
def get_penalty_details(against_loan):
|
||||||
|
penalty_details = frappe.db.sql("""
|
||||||
|
SELECT posting_date, (penalty_amount - total_penalty_paid) as pending_penalty_amount
|
||||||
|
FROM `tabLoan Repayment` where posting_date >= (SELECT MAX(posting_date) from `tabLoan Repayment`
|
||||||
|
where against_loan = %s) and docstatus = 1 and against_loan = %s
|
||||||
|
""", (against_loan, against_loan))
|
||||||
|
|
||||||
|
if penalty_details:
|
||||||
|
return penalty_details[0][0], flt(penalty_details[0][1])
|
||||||
|
else:
|
||||||
|
return None, 0
|
||||||
|
|
||||||
# This function returns the amounts that are payable at the time of loan repayment based on posting date
|
# This function returns the amounts that are payable at the time of loan repayment based on posting date
|
||||||
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
|
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
|
||||||
|
|
||||||
@ -335,8 +362,9 @@ def get_amounts(amounts, against_loan, posting_date):
|
|||||||
|
|
||||||
against_loan_doc = frappe.get_doc("Loan", against_loan)
|
against_loan_doc = frappe.get_doc("Loan", against_loan)
|
||||||
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
|
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
|
||||||
accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name)
|
accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name, posting_date)
|
||||||
|
|
||||||
|
computed_penalty_date, pending_penalty_amount = get_penalty_details(against_loan)
|
||||||
pending_accrual_entries = {}
|
pending_accrual_entries = {}
|
||||||
|
|
||||||
total_pending_interest = 0
|
total_pending_interest = 0
|
||||||
@ -351,8 +379,13 @@ def get_amounts(amounts, against_loan, posting_date):
|
|||||||
# and if no_of_late days are positive then penalty is levied
|
# and if no_of_late days are positive then penalty is levied
|
||||||
|
|
||||||
due_date = add_days(entry.posting_date, 1)
|
due_date = add_days(entry.posting_date, 1)
|
||||||
no_of_late_days = date_diff(posting_date,
|
due_date_after_grace_period = add_days(due_date, loan_type_details.grace_period_in_days)
|
||||||
add_days(due_date, loan_type_details.grace_period_in_days)) + 1
|
|
||||||
|
# Consider one day after already calculated penalty
|
||||||
|
if computed_penalty_date and getdate(computed_penalty_date) >= due_date_after_grace_period:
|
||||||
|
due_date_after_grace_period = add_days(computed_penalty_date, 1)
|
||||||
|
|
||||||
|
no_of_late_days = date_diff(posting_date, due_date_after_grace_period) + 1
|
||||||
|
|
||||||
if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
|
if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
|
||||||
penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
|
penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
|
||||||
@ -390,7 +423,7 @@ def get_amounts(amounts, against_loan, posting_date):
|
|||||||
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
|
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
|
||||||
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
|
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
|
||||||
amounts["interest_amount"] = flt(total_pending_interest, precision)
|
amounts["interest_amount"] = flt(total_pending_interest, precision)
|
||||||
amounts["penalty_amount"] = flt(penalty_amount, precision)
|
amounts["penalty_amount"] = flt(penalty_amount + pending_penalty_amount, precision)
|
||||||
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
|
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
|
||||||
amounts["pending_accrual_entries"] = pending_accrual_entries
|
amounts["pending_accrual_entries"] = pending_accrual_entries
|
||||||
amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
|
amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import get_datetime, flt
|
from frappe.utils import get_datetime, flt, getdate
|
||||||
import json
|
import json
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
|
from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
|
||||||
@ -113,7 +113,11 @@ class LoanSecurityUnpledge(Document):
|
|||||||
pledged_qty += qty
|
pledged_qty += qty
|
||||||
|
|
||||||
if not pledged_qty:
|
if not pledged_qty:
|
||||||
frappe.db.set_value('Loan', self.loan, 'status', 'Closed')
|
frappe.db.set_value('Loan', self.loan,
|
||||||
|
{
|
||||||
|
'status': 'Closed',
|
||||||
|
'closure_date': getdate()
|
||||||
|
})
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_pledged_security_qty(loan):
|
def get_pledged_security_qty(loan):
|
||||||
|
@ -70,7 +70,9 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "loan_repayment_entry",
|
"fieldname": "loan_repayment_entry",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Loan Repayment Entry",
|
"label": "Loan Repayment Entry",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Loan Repayment",
|
"options": "Loan Repayment",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -83,9 +85,10 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-16 13:17:04.798335",
|
"modified": "2021-03-14 20:47:11.725818",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Salary Slip Loan",
|
"name": "Salary Slip Loan",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr, flt
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
||||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||||
@ -81,15 +81,27 @@ class TestBOM(unittest.TestCase):
|
|||||||
bom = frappe.copy_doc(test_records[2])
|
bom = frappe.copy_doc(test_records[2])
|
||||||
bom.insert()
|
bom.insert()
|
||||||
|
|
||||||
# test amounts in selected currency
|
raw_material_cost = 0.0
|
||||||
self.assertEqual(bom.operating_cost, 100)
|
op_cost = 0.0
|
||||||
self.assertEqual(bom.raw_material_cost, 351.68)
|
|
||||||
self.assertEqual(bom.total_cost, 451.68)
|
for op_row in bom.operations:
|
||||||
|
op_cost += op_row.operating_cost
|
||||||
|
|
||||||
|
for row in bom.items:
|
||||||
|
raw_material_cost += row.amount
|
||||||
|
|
||||||
|
base_raw_material_cost = raw_material_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
|
||||||
|
base_op_cost = op_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
|
||||||
|
|
||||||
|
# test amounts in selected currency, almostEqual checks for 7 digits by default
|
||||||
|
self.assertAlmostEqual(bom.operating_cost, op_cost)
|
||||||
|
self.assertAlmostEqual(bom.raw_material_cost, raw_material_cost)
|
||||||
|
self.assertAlmostEqual(bom.total_cost, raw_material_cost + op_cost)
|
||||||
|
|
||||||
# test amounts in selected currency
|
# test amounts in selected currency
|
||||||
self.assertEqual(bom.base_operating_cost, 6000)
|
self.assertAlmostEqual(bom.base_operating_cost, base_op_cost)
|
||||||
self.assertEqual(bom.base_raw_material_cost, 21100.80)
|
self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
|
||||||
self.assertEqual(bom.base_total_cost, 27100.80)
|
self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
|
||||||
|
|
||||||
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
|
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
|
||||||
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
|
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
|
||||||
|
@ -47,6 +47,8 @@ class JobCard(Document):
|
|||||||
if d.completed_qty:
|
if d.completed_qty:
|
||||||
self.total_completed_qty += d.completed_qty
|
self.total_completed_qty += d.completed_qty
|
||||||
|
|
||||||
|
self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
|
||||||
|
|
||||||
def get_overlap_for(self, args, check_next_available_slot=False):
|
def get_overlap_for(self, args, check_next_available_slot=False):
|
||||||
production_capacity = 1
|
production_capacity = 1
|
||||||
|
|
||||||
@ -431,6 +433,7 @@ def make_material_request(source_name, target_doc=None):
|
|||||||
def make_stock_entry(source_name, target_doc=None):
|
def make_stock_entry(source_name, target_doc=None):
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(obj, target, source_parent):
|
||||||
target.t_warehouse = source_parent.wip_warehouse
|
target.t_warehouse = source_parent.wip_warehouse
|
||||||
|
target.conversion_factor = 1
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.purpose = "Material Transfer for Manufacture"
|
target.purpose = "Material Transfer for Manufacture"
|
||||||
|
@ -11,10 +11,9 @@ frappe.ui.form.on('Routing', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
display_sequence_id_column: function(frm) {
|
display_sequence_id_column: function(frm) {
|
||||||
frappe.meta.get_docfield("BOM Operation", "sequence_id",
|
frm.fields_dict.operations.grid.update_docfield_property(
|
||||||
frm.doc.name).in_list_view = true;
|
'sequence_id', 'in_list_view', 1
|
||||||
|
);
|
||||||
frm.fields_dict.operations.grid.refresh();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
calculate_operating_cost: function(frm, child) {
|
calculate_operating_cost: function(frm, child) {
|
||||||
@ -69,4 +68,4 @@ frappe.ui.form.on('BOM Operation', {
|
|||||||
const d = locals[cdt][cdn];
|
const d = locals[cdt][cdn];
|
||||||
frm.events.calculate_operating_cost(frm, d);
|
frm.events.calculate_operating_cost(frm, d);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing",
|
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"modified": "2020-07-08 14:05:56.197563",
|
"modified": "2020-06-29 20:25:36.899106",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Manufacturing",
|
"name": "Manufacturing",
|
||||||
@ -53,4 +53,4 @@
|
|||||||
"subtitle": "Products, Raw Materials, BOM, Work Order, and more.",
|
"subtitle": "Products, Raw Materials, BOM, Work Order, and more.",
|
||||||
"success_message": "Manufacturing module is all set up!",
|
"success_message": "Manufacturing module is all set up!",
|
||||||
"title": "Let's Set Up the Manufacturing Module."
|
"title": "Let's Set Up the Manufacturing Module."
|
||||||
}
|
}
|
||||||
|
@ -756,11 +756,19 @@ erpnext.patches.v13_0.update_payment_terms_outstanding
|
|||||||
erpnext.patches.v12_0.add_state_code_for_ladakh
|
erpnext.patches.v12_0.add_state_code_for_ladakh
|
||||||
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
||||||
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
||||||
erpnext.patches.v13_0.update_vehicle_no_reqd_condition
|
erpnext.patches.v12_0.update_vehicle_no_reqd_condition
|
||||||
|
erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17
|
||||||
|
erpnext.patches.v12_0.add_einvoice_summary_report_permissions
|
||||||
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
||||||
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
||||||
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
||||||
erpnext.patches.v13_0.setup_uae_vat_fields
|
erpnext.patches.v13_0.setup_uae_vat_fields
|
||||||
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
||||||
|
erpnext.patches.v12_0.add_company_link_to_einvoice_settings
|
||||||
erpnext.patches.v13_0.rename_discharge_date_in_ip_record
|
erpnext.patches.v13_0.rename_discharge_date_in_ip_record
|
||||||
|
erpnext.patches.v12_0.create_taxable_value_field
|
||||||
|
erpnext.patches.v12_0.add_gst_category_in_delivery_note
|
||||||
erpnext.patches.v12_0.purchase_receipt_status
|
erpnext.patches.v12_0.purchase_receipt_status
|
||||||
|
erpnext.patches.v13_0.fix_non_unique_represents_company
|
||||||
|
erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
|
||||||
|
erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company or not frappe.db.count('E Invoice User'):
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc("regional", "doctype", "e_invoice_user")
|
||||||
|
for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']):
|
||||||
|
company_name = frappe.db.sql("""
|
||||||
|
select dl.link_name from `tabAddress` a, `tabDynamic Link` dl
|
||||||
|
where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company'
|
||||||
|
""", (creds.get('gstin')))
|
||||||
|
if company_name and len(company_name) > 0:
|
||||||
|
frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0])
|
@ -0,0 +1,18 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'Italy'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
custom_fields = {
|
||||||
|
'Sales Invoice': [
|
||||||
|
dict(fieldname='type_of_document', label='Type of Document',
|
||||||
|
fieldtype='Select', insert_after='customer_fiscal_code',
|
||||||
|
options='\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27'),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
create_custom_fields(custom_fields, update=True)
|
69
erpnext/patches/v12_0/add_einvoice_status_field.py
Normal file
69
erpnext/patches/v12_0/add_einvoice_status_field.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import json
|
||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
# move hidden einvoice fields to a different section
|
||||||
|
custom_fields = {
|
||||||
|
'Sales Invoice': [
|
||||||
|
dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
|
||||||
|
print_hide=1, hidden=1),
|
||||||
|
|
||||||
|
dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
|
||||||
|
no_copy=1, print_hide=1),
|
||||||
|
|
||||||
|
dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
|
||||||
|
|
||||||
|
dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date',
|
||||||
|
no_copy=1, print_hide=1),
|
||||||
|
|
||||||
|
dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
|
||||||
|
no_copy=1, print_hide=1, read_only=1),
|
||||||
|
|
||||||
|
dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
|
||||||
|
no_copy=1, print_hide=1, read_only=1),
|
||||||
|
|
||||||
|
dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
|
||||||
|
no_copy=1, print_hide=1, read_only=1),
|
||||||
|
|
||||||
|
dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
|
||||||
|
options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
|
||||||
|
|
||||||
|
dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
|
||||||
|
hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
create_custom_fields(custom_fields, update=True)
|
||||||
|
|
||||||
|
if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'):
|
||||||
|
frappe.db.sql('''
|
||||||
|
UPDATE `tabSales Invoice` SET einvoice_status = 'Pending'
|
||||||
|
WHERE
|
||||||
|
posting_date >= '2021-04-01'
|
||||||
|
AND ifnull(irn, '') = ''
|
||||||
|
AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '')
|
||||||
|
AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export')
|
||||||
|
''')
|
||||||
|
|
||||||
|
# set appropriate statuses
|
||||||
|
frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated'
|
||||||
|
WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''')
|
||||||
|
|
||||||
|
frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled'
|
||||||
|
WHERE ifnull(irn_cancelled, 0) = 1''')
|
||||||
|
|
||||||
|
# set correct acknowledgement in e-invoices
|
||||||
|
einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice'])
|
||||||
|
|
||||||
|
if einvoices:
|
||||||
|
for inv in einvoices:
|
||||||
|
signed_einvoice = inv.get('signed_einvoice')
|
||||||
|
if signed_einvoice:
|
||||||
|
signed_einvoice = json.loads(signed_einvoice)
|
||||||
|
frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False)
|
||||||
|
frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False)
|
@ -0,0 +1,18 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
if frappe.db.exists('Report', 'E-Invoice Summary') and \
|
||||||
|
not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')):
|
||||||
|
frappe.get_doc(dict(
|
||||||
|
doctype='Custom Role',
|
||||||
|
report='E-Invoice Summary',
|
||||||
|
roles= [
|
||||||
|
dict(role='Accounts User'),
|
||||||
|
dict(role='Accounts Manager')
|
||||||
|
]
|
||||||
|
)).insert()
|
19
erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
Normal file
19
erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
custom_fields = {
|
||||||
|
'Delivery Note': [
|
||||||
|
dict(fieldname='gst_category', label='GST Category',
|
||||||
|
fieldtype='Select', insert_after='gst_vehicle_type', print_hide=1,
|
||||||
|
options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
|
||||||
|
fetch_from='customer.gst_category', fetch_if_empty=1),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
create_custom_fields(custom_fields, update=True)
|
18
erpnext/patches/v12_0/create_taxable_value_field.py
Normal file
18
erpnext/patches/v12_0/create_taxable_value_field.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
custom_fields = {
|
||||||
|
'Sales Invoice Item': [
|
||||||
|
dict(fieldname='taxable_value', label='Taxable Value',
|
||||||
|
fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
|
||||||
|
print_hide=1)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
create_custom_fields(custom_fields, update=True)
|
@ -1,6 +1,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
frappe.reload_doc('custom', 'doctype', 'custom_field')
|
||||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
@ -8,36 +8,39 @@ from erpnext.regional.india.setup import setup
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
|
||||||
doctypes = ['salary_component',
|
doctypes = ['salary_component',
|
||||||
'Employee Tax Exemption Declaration',
|
'Employee Tax Exemption Declaration',
|
||||||
'Employee Tax Exemption Proof Submission',
|
'Employee Tax Exemption Proof Submission',
|
||||||
'Employee Tax Exemption Declaration Category',
|
'Employee Tax Exemption Declaration Category',
|
||||||
'Employee Tax Exemption Proof Submission Detail'
|
'Employee Tax Exemption Proof Submission Detail',
|
||||||
]
|
'gratuity_rule',
|
||||||
|
'gratuity_rule_slab',
|
||||||
|
'gratuity_applicable_component'
|
||||||
|
]
|
||||||
|
|
||||||
for doctype in doctypes:
|
for doctype in doctypes:
|
||||||
frappe.reload_doc('Payroll', 'doctype', doctype)
|
frappe.reload_doc('Payroll', 'doctype', doctype)
|
||||||
|
|
||||||
|
|
||||||
reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
|
reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
|
||||||
for report in reports:
|
for report in reports:
|
||||||
frappe.reload_doc('Regional', 'Report', report)
|
frappe.reload_doc('Regional', 'Report', report)
|
||||||
frappe.reload_doc('Regional', 'Report', report)
|
frappe.reload_doc('Regional', 'Report', report)
|
||||||
|
|
||||||
if erpnext.get_region() == "India":
|
if erpnext.get_region() == "India":
|
||||||
setup(patch=True)
|
setup(patch=True)
|
||||||
|
|
||||||
if frappe.db.exists("Salary Component", "Income Tax"):
|
if frappe.db.exists("Salary Component", "Income Tax"):
|
||||||
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
|
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
|
||||||
if frappe.db.exists("Salary Component", "TDS"):
|
if frappe.db.exists("Salary Component", "TDS"):
|
||||||
frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1)
|
frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1)
|
||||||
|
|
||||||
components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1)
|
components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1)
|
||||||
for component in components:
|
for component in components:
|
||||||
frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1)
|
frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1)
|
||||||
|
|
||||||
if erpnext.get_region() == "India":
|
if erpnext.get_region() == "India":
|
||||||
if frappe.db.exists("Salary Component", "Provident Fund"):
|
if frappe.db.exists("Salary Component", "Provident Fund"):
|
||||||
frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund")
|
frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund")
|
||||||
if frappe.db.exists("Salary Component", "Professional Tax"):
|
if frappe.db.exists("Salary Component", "Professional Tax"):
|
||||||
frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax")
|
frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax")
|
@ -11,4 +11,8 @@ def execute():
|
|||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'pos_invoice')
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'pos_invoice_item')
|
||||||
|
|
||||||
make_custom_fields()
|
make_custom_fields()
|
@ -0,0 +1,8 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.db.sql("""
|
||||||
|
update tabCustomer
|
||||||
|
set represents_company = NULL
|
||||||
|
where represents_company = ''
|
||||||
|
""")
|
@ -18,6 +18,7 @@ def execute():
|
|||||||
|
|
||||||
for old_dt, new_dt in doctypes.items():
|
for old_dt, new_dt in doctypes.items():
|
||||||
if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt):
|
if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt):
|
||||||
|
frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt))
|
||||||
frappe.rename_doc('DocType', old_dt, new_dt, force=True)
|
frappe.rename_doc('DocType', old_dt, new_dt, force=True)
|
||||||
frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
|
frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
|
||||||
frappe.delete_doc_if_exists('DocType', old_dt)
|
frappe.delete_doc_if_exists('DocType', old_dt)
|
||||||
@ -36,6 +37,18 @@ def execute():
|
|||||||
SET parentfield = %(parentfield)s
|
SET parentfield = %(parentfield)s
|
||||||
""".format(doctype), {'parentfield': parentfield})
|
""".format(doctype), {'parentfield': parentfield})
|
||||||
|
|
||||||
|
# copy renamed child table fields (fields were already renamed in old doctype json, hence sql)
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_name = test_name""")
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_event = test_event""")
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_uom = test_uom""")
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_comment = test_comment""")
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_event = test_event""")
|
||||||
|
frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_uom = test_uom""")
|
||||||
|
frappe.db.sql("""UPDATE `tabDescriptive Test Result` SET lab_test_particulars = test_particulars""")
|
||||||
|
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_template = test_template""")
|
||||||
|
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_description = test_description""")
|
||||||
|
frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_rate = test_rate""")
|
||||||
|
|
||||||
# rename field
|
# rename field
|
||||||
frappe.reload_doc('healthcare', 'doctype', 'lab_test')
|
frappe.reload_doc('healthcare', 'doctype', 'lab_test')
|
||||||
if frappe.db.has_column('Lab Test', 'special_toggle'):
|
if frappe.db.has_column('Lab Test', 'special_toggle'):
|
||||||
|
@ -20,9 +20,11 @@ def execute():
|
|||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
frappe.flags.warehouse_account_map = {}
|
frappe.flags.warehouse_account_map = {}
|
||||||
|
|
||||||
|
company_list = []
|
||||||
|
|
||||||
data = frappe.db.sql('''
|
data = frappe.db.sql('''
|
||||||
SELECT
|
SELECT
|
||||||
name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
|
name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company
|
||||||
FROM
|
FROM
|
||||||
`tabStock Ledger Entry`
|
`tabStock Ledger Entry`
|
||||||
WHERE
|
WHERE
|
||||||
@ -36,6 +38,9 @@ def execute():
|
|||||||
total_sle = len(data)
|
total_sle = len(data)
|
||||||
i = 0
|
i = 0
|
||||||
for d in data:
|
for d in data:
|
||||||
|
if d.company not in company_list:
|
||||||
|
company_list.append(d.company)
|
||||||
|
|
||||||
update_entries_after({
|
update_entries_after({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
@ -53,8 +58,10 @@ def execute():
|
|||||||
|
|
||||||
print("Reposting General Ledger Entries...")
|
print("Reposting General Ledger Entries...")
|
||||||
|
|
||||||
for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
|
if data:
|
||||||
update_gl_entries_after(posting_date, posting_time, company=row.name)
|
for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
|
||||||
|
if row.name in company_list:
|
||||||
|
update_gl_entries_after(posting_date, posting_time, company=row.name)
|
||||||
|
|
||||||
frappe.db.auto_commit_on_many_writes = 0
|
frappe.db.auto_commit_on_many_writes = 0
|
||||||
|
|
||||||
|
24
erpnext/patches/v13_0/make_non_standard_user_type.py
Normal file
24
erpnext/patches/v13_0/make_non_standard_user_type.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from six import iteritems
|
||||||
|
from erpnext.setup.install import add_non_standard_user_types
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
doctype_dict = {
|
||||||
|
'projects': ['Timesheet'],
|
||||||
|
'payroll': ['Salary Slip', 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission'],
|
||||||
|
'hr': ['Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request']
|
||||||
|
}
|
||||||
|
|
||||||
|
for module, doctypes in iteritems(doctype_dict):
|
||||||
|
for doctype in doctypes:
|
||||||
|
frappe.reload_doc(module, 'doctype', doctype)
|
||||||
|
|
||||||
|
|
||||||
|
frappe.flags.ignore_select_perm = True
|
||||||
|
frappe.flags.update_select_perm_after_migrate = True
|
||||||
|
|
||||||
|
add_non_standard_user_types()
|
@ -6,8 +6,9 @@ def execute():
|
|||||||
if "Healthcare" not in frappe.get_active_domains():
|
if "Healthcare" not in frappe.get_active_domains():
|
||||||
return
|
return
|
||||||
|
|
||||||
frappe.reload_doc("healthcare", "doctype", "Therapy Session")
|
|
||||||
frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order")
|
frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order")
|
||||||
|
frappe.reload_doc("healthcare", "doctype", "Therapy Session")
|
||||||
|
frappe.reload_doc("healthcare", "doctype", "Clinical Procedure")
|
||||||
frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
|
frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
|
||||||
frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
|
frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
|
||||||
frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")
|
frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from erpnext.regional.united_arab_emirates.setup import setup
|
from erpnext.regional.united_arab_emirates.setup import setup
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
|
company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
setup()
|
frappe.reload_doc('regional', 'report', 'uae_vat_201')
|
||||||
|
frappe.reload_doc('regional', 'doctype', 'uae_vat_settings')
|
||||||
|
frappe.reload_doc('regional', 'doctype', 'uae_vat_account')
|
||||||
|
|
||||||
|
setup()
|
||||||
|
@ -5,7 +5,7 @@ from frappe import _
|
|||||||
def execute():
|
def execute():
|
||||||
from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources
|
from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources
|
||||||
|
|
||||||
frappe.reload_doc('selling', 'doctype', 'lead_source')
|
frappe.reload_doc('crm', 'doctype', 'lead_source')
|
||||||
|
|
||||||
frappe.local.lang = frappe.db.get_default("lang") or 'en'
|
frappe.local.lang = frappe.db.get_default("lang") or 'en'
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:45:48.566756",
|
"modified": "2021-03-31 22:33:59.098532",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Additional Salary",
|
"name": "Additional Salary",
|
||||||
|
@ -147,7 +147,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:46:22.465521",
|
"modified": "2021-03-31 22:35:08.940087",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application",
|
"name": "Employee Benefit Application",
|
||||||
|
@ -144,7 +144,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 15:51:51.489269",
|
"modified": "2021-03-31 22:37:21.024625",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Claim",
|
"name": "Employee Benefit Claim",
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:48:00.919839",
|
"modified": "2021-03-31 22:38:20.332316",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Incentive",
|
"name": "Employee Incentive",
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 20:41:57.387749",
|
"modified": "2021-03-31 22:39:59.237361",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Declaration",
|
"name": "Employee Tax Exemption Declaration",
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 20:48:32.639885",
|
"modified": "2021-03-31 22:41:13.723339",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Proof Submission",
|
"name": "Employee Tax Exemption Proof Submission",
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 20:53:33.323712",
|
"modified": "2021-03-31 22:42:08.139520",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Income Tax Slab",
|
"name": "Income Tax Slab",
|
||||||
|
@ -133,45 +133,59 @@ frappe.ui.form.on('Payroll Entry', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query('employee', 'employees', () => {
|
||||||
|
if (!frm.doc.company) {
|
||||||
|
frappe.msgprint(__("Please set a Company"));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query",
|
||||||
|
filters: frm.events.get_employee_filters(frm)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get_employee_filters: function (frm) {
|
||||||
|
let filters = {};
|
||||||
|
filters['company'] = frm.doc.company;
|
||||||
|
filters['start_date'] = frm.doc.start_date;
|
||||||
|
filters['end_date'] = frm.doc.end_date;
|
||||||
|
|
||||||
|
if (frm.doc.department) {
|
||||||
|
filters['department'] = frm.doc.department;
|
||||||
|
}
|
||||||
|
if (frm.doc.branch) {
|
||||||
|
filters['branch'] = frm.doc.branch;
|
||||||
|
}
|
||||||
|
if (frm.doc.designation) {
|
||||||
|
filters['designation'] = frm.doc.designation;
|
||||||
|
}
|
||||||
|
if (frm.doc.employees) {
|
||||||
|
filters['employees'] = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
|
||||||
|
}
|
||||||
|
return filters;
|
||||||
},
|
},
|
||||||
|
|
||||||
payroll_frequency: function (frm) {
|
payroll_frequency: function (frm) {
|
||||||
frm.trigger("set_start_end_dates").then( ()=> {
|
frm.trigger("set_start_end_dates").then( ()=> {
|
||||||
frm.events.clear_employee_table(frm);
|
frm.events.clear_employee_table(frm);
|
||||||
frm.events.get_employee_with_salary_slip_and_set_query(frm);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
employee_filters: function (frm, emp_list) {
|
|
||||||
frm.set_query('employee', 'employees', () => {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
name: ["not in", emp_list]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
get_employee_with_salary_slip_and_set_query: function (frm) {
|
|
||||||
frappe.db.get_list('Salary Slip', {
|
|
||||||
filters: {
|
|
||||||
start_date: frm.doc.start_date,
|
|
||||||
end_date: frm.doc.end_date,
|
|
||||||
docstatus: 1,
|
|
||||||
},
|
|
||||||
fields: ['employee']
|
|
||||||
}).then((emp) => {
|
|
||||||
var emp_list = [];
|
|
||||||
emp.forEach((employee_data) => {
|
|
||||||
emp_list.push(Object.values(employee_data)[0]);
|
|
||||||
});
|
|
||||||
frm.events.employee_filters(frm, emp_list);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
company: function (frm) {
|
company: function (frm) {
|
||||||
frm.events.clear_employee_table(frm);
|
frm.events.clear_employee_table(frm);
|
||||||
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
||||||
|
frm.trigger("set_payable_account_and_currency");
|
||||||
|
},
|
||||||
|
|
||||||
|
set_payable_account_and_currency: function (frm) {
|
||||||
|
frappe.db.get_value("Company", {"name": frm.doc.company}, "default_currency", (r) => {
|
||||||
|
frm.set_value('currency', r.default_currency);
|
||||||
|
});
|
||||||
|
frappe.db.get_value("Company", {"name": frm.doc.company}, "default_payroll_payable_account", (r) => {
|
||||||
|
frm.set_value('payroll_payable_account', r.default_payroll_payable_account);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
currency: function (frm) {
|
currency: function (frm) {
|
||||||
@ -345,11 +359,3 @@ let render_employee_attendance = function (frm, data) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
frappe.ui.form.on('Payroll Employee Detail', {
|
|
||||||
employee: function(frm) {
|
|
||||||
if (!frm.doc.payroll_frequency) {
|
|
||||||
frappe.throw(__("Please set a Payroll Frequency"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
@ -10,16 +10,17 @@ from frappe.utils import cint, flt, add_days, getdate, add_to_date, DATE_FORMAT,
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
from frappe.desk.reportview import get_match_cond, get_filters_cond
|
||||||
|
|
||||||
class PayrollEntry(Document):
|
class PayrollEntry(Document):
|
||||||
def onload(self):
|
def onload(self):
|
||||||
if not self.docstatus==1 or self.salary_slips_submitted:
|
if not self.docstatus==1 or self.salary_slips_submitted:
|
||||||
return
|
return
|
||||||
|
|
||||||
# check if salary slips were manually submitted
|
# check if salary slips were manually submitted
|
||||||
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
|
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
|
||||||
if cint(entries) == len(self.employees):
|
if cint(entries) == len(self.employees):
|
||||||
self.set_onload("submitted_ss", True)
|
self.set_onload("submitted_ss", True)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.number_of_employees = len(self.employees)
|
self.number_of_employees = len(self.employees)
|
||||||
@ -59,16 +60,16 @@ class PayrollEntry(Document):
|
|||||||
condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
|
condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
|
||||||
|
|
||||||
sal_struct = frappe.db.sql_list("""
|
sal_struct = frappe.db.sql_list("""
|
||||||
select
|
select
|
||||||
name from `tabSalary Structure`
|
name from `tabSalary Structure`
|
||||||
where
|
where
|
||||||
docstatus = 1 and
|
docstatus = 1 and
|
||||||
is_active = 'Yes'
|
is_active = 'Yes'
|
||||||
and company = %(company)s
|
and company = %(company)s
|
||||||
and currency = %(currency)s and
|
and currency = %(currency)s and
|
||||||
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
|
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
|
||||||
{condition}""".format(condition=condition),
|
{condition}""".format(condition=condition),
|
||||||
{"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
|
{"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
|
||||||
|
|
||||||
if sal_struct:
|
if sal_struct:
|
||||||
cond += "and t2.salary_structure IN %(sal_struct)s "
|
cond += "and t2.salary_structure IN %(sal_struct)s "
|
||||||
@ -176,15 +177,15 @@ class PayrollEntry(Document):
|
|||||||
"""
|
"""
|
||||||
Returns list of salary slips based on selected criteria
|
Returns list of salary slips based on selected criteria
|
||||||
"""
|
"""
|
||||||
cond = self.get_filter_condition()
|
|
||||||
|
|
||||||
ss_list = frappe.db.sql("""
|
ss_list = frappe.db.sql("""
|
||||||
select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
|
select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
|
||||||
where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
|
where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s
|
||||||
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
|
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s
|
||||||
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
|
""", (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict)
|
||||||
return ss_list
|
return ss_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def submit_salary_slips(self):
|
def submit_salary_slips(self):
|
||||||
self.check_permission('write')
|
self.check_permission('write')
|
||||||
ss_list = self.get_sal_slip_list(ss_status=0)
|
ss_list = self.get_sal_slip_list(ss_status=0)
|
||||||
@ -270,26 +271,26 @@ class PayrollEntry(Document):
|
|||||||
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
|
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
|
||||||
payable_amount += flt(amount, precision)
|
payable_amount += flt(amount, precision)
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": acc_cc[0],
|
"account": acc_cc[0],
|
||||||
"debit_in_account_currency": flt(amt, precision),
|
"debit_in_account_currency": flt(amt, precision),
|
||||||
"exchange_rate": flt(exchange_rate),
|
"exchange_rate": flt(exchange_rate),
|
||||||
"party_type": '',
|
"party_type": '',
|
||||||
"cost_center": acc_cc[1] or self.cost_center,
|
"cost_center": acc_cc[1] or self.cost_center,
|
||||||
"project": self.project
|
"project": self.project
|
||||||
})
|
})
|
||||||
|
|
||||||
# Deductions
|
# Deductions
|
||||||
for acc_cc, amount in deductions.items():
|
for acc_cc, amount in deductions.items():
|
||||||
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
|
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
|
||||||
payable_amount -= flt(amount, precision)
|
payable_amount -= flt(amount, precision)
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": acc_cc[0],
|
"account": acc_cc[0],
|
||||||
"credit_in_account_currency": flt(amt, precision),
|
"credit_in_account_currency": flt(amt, precision),
|
||||||
"exchange_rate": flt(exchange_rate),
|
"exchange_rate": flt(exchange_rate),
|
||||||
"cost_center": acc_cc[1] or self.cost_center,
|
"cost_center": acc_cc[1] or self.cost_center,
|
||||||
"party_type": '',
|
"party_type": '',
|
||||||
"project": self.project
|
"project": self.project
|
||||||
})
|
})
|
||||||
|
|
||||||
# Payable amount
|
# Payable amount
|
||||||
exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies)
|
exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies)
|
||||||
@ -335,10 +336,9 @@ class PayrollEntry(Document):
|
|||||||
def make_payment_entry(self):
|
def make_payment_entry(self):
|
||||||
self.check_permission('write')
|
self.check_permission('write')
|
||||||
|
|
||||||
cond = self.get_filter_condition()
|
|
||||||
salary_slip_name_list = frappe.db.sql(""" select t1.name from `tabSalary Slip` t1
|
salary_slip_name_list = frappe.db.sql(""" select t1.name from `tabSalary Slip` t1
|
||||||
where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
|
where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s
|
||||||
""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_list = True)
|
""", (self.start_date, self.end_date, self.name), as_list = True)
|
||||||
|
|
||||||
if salary_slip_name_list and len(salary_slip_name_list) > 0:
|
if salary_slip_name_list and len(salary_slip_name_list) > 0:
|
||||||
salary_slip_total = 0
|
salary_slip_total = 0
|
||||||
@ -370,20 +370,20 @@ class PayrollEntry(Document):
|
|||||||
|
|
||||||
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies)
|
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies)
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": self.payment_account,
|
"account": self.payment_account,
|
||||||
"bank_account": self.bank_account,
|
"bank_account": self.bank_account,
|
||||||
"credit_in_account_currency": flt(amount, precision),
|
"credit_in_account_currency": flt(amount, precision),
|
||||||
"exchange_rate": flt(exchange_rate),
|
"exchange_rate": flt(exchange_rate),
|
||||||
})
|
})
|
||||||
|
|
||||||
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies)
|
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies)
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": payroll_payable_account,
|
"account": payroll_payable_account,
|
||||||
"debit_in_account_currency": flt(amount, precision),
|
"debit_in_account_currency": flt(amount, precision),
|
||||||
"exchange_rate": flt(exchange_rate),
|
"exchange_rate": flt(exchange_rate),
|
||||||
"reference_type": self.doctype,
|
"reference_type": self.doctype,
|
||||||
"reference_name": self.name
|
"reference_name": self.name
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(currencies) > 1:
|
if len(currencies) > 1:
|
||||||
multi_currency = 1
|
multi_currency = 1
|
||||||
@ -409,6 +409,7 @@ class PayrollEntry(Document):
|
|||||||
self.update(get_start_end_dates(self.payroll_frequency,
|
self.update(get_start_end_dates(self.payroll_frequency,
|
||||||
self.start_date or self.posting_date, self.company))
|
self.start_date or self.posting_date, self.company))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def validate_employee_attendance(self):
|
def validate_employee_attendance(self):
|
||||||
employees_to_mark_attendance = []
|
employees_to_mark_attendance = []
|
||||||
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
|
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
|
||||||
@ -424,7 +425,7 @@ class PayrollEntry(Document):
|
|||||||
employees_to_mark_attendance.append({
|
employees_to_mark_attendance.append({
|
||||||
"employee": employee_detail.employee,
|
"employee": employee_detail.employee,
|
||||||
"employee_name": employee_detail.employee_name
|
"employee_name": employee_detail.employee_name
|
||||||
})
|
})
|
||||||
return employees_to_mark_attendance
|
return employees_to_mark_attendance
|
||||||
|
|
||||||
def get_count_holidays_of_employee(self, employee, start_date):
|
def get_count_holidays_of_employee(self, employee, start_date):
|
||||||
@ -441,11 +442,11 @@ class PayrollEntry(Document):
|
|||||||
def get_count_employee_attendance(self, employee, start_date):
|
def get_count_employee_attendance(self, employee, start_date):
|
||||||
marked_days = 0
|
marked_days = 0
|
||||||
attendances = frappe.get_all("Attendance",
|
attendances = frappe.get_all("Attendance",
|
||||||
fields = ["count(*)"],
|
fields = ["count(*)"],
|
||||||
filters = {
|
filters = {
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
"attendance_date": ('between', [start_date, self.end_date])
|
"attendance_date": ('between', [start_date, self.end_date])
|
||||||
}, as_list=1)
|
}, as_list=1)
|
||||||
if attendances and attendances[0][0]:
|
if attendances and attendances[0][0]:
|
||||||
marked_days = attendances[0][0]
|
marked_days = attendances[0][0]
|
||||||
return marked_days
|
return marked_days
|
||||||
@ -553,6 +554,7 @@ def payroll_entry_has_bank_entries(name):
|
|||||||
def create_salary_slips_for_employees(employees, args, publish_progress=True):
|
def create_salary_slips_for_employees(employees, args, publish_progress=True):
|
||||||
salary_slips_exists_for = get_existing_salary_slips(employees, args)
|
salary_slips_exists_for = get_existing_salary_slips(employees, args)
|
||||||
count=0
|
count=0
|
||||||
|
salary_slips_not_created = []
|
||||||
for emp in employees:
|
for emp in employees:
|
||||||
if emp not in salary_slips_exists_for:
|
if emp not in salary_slips_exists_for:
|
||||||
args.update({
|
args.update({
|
||||||
@ -565,34 +567,26 @@ def create_salary_slips_for_employees(employees, args, publish_progress=True):
|
|||||||
if publish_progress:
|
if publish_progress:
|
||||||
frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
|
frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
|
||||||
title = _("Creating Salary Slips..."))
|
title = _("Creating Salary Slips..."))
|
||||||
else:
|
|
||||||
salary_slip_name = frappe.db.sql(
|
|
||||||
'''SELECT
|
|
||||||
name
|
|
||||||
FROM `tabSalary Slip`
|
|
||||||
WHERE company=%s
|
|
||||||
AND start_date >= %s
|
|
||||||
AND end_date <= %s
|
|
||||||
AND employee = %s
|
|
||||||
''', (args.company, args.start_date, args.end_date, emp), as_dict=True)
|
|
||||||
|
|
||||||
salary_slip_doc = frappe.get_doc('Salary Slip', salary_slip_name[0].name)
|
else:
|
||||||
salary_slip_doc.exchange_rate = args.exchange_rate
|
salary_slips_not_created.append(emp)
|
||||||
salary_slip_doc.set_totals()
|
|
||||||
salary_slip_doc.db_update()
|
|
||||||
|
|
||||||
payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
|
payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
|
||||||
payroll_entry.db_set("salary_slips_created", 1)
|
payroll_entry.db_set("salary_slips_created", 1)
|
||||||
payroll_entry.notify_update()
|
payroll_entry.notify_update()
|
||||||
|
|
||||||
|
if salary_slips_not_created:
|
||||||
|
frappe.msgprint(_("Salary Slips already exists for employees {}, and will not be processed by this payroll.")
|
||||||
|
.format(frappe.bold(", ".join([emp for emp in salary_slips_not_created]))) , title=_("Message"), indicator="orange")
|
||||||
|
|
||||||
def get_existing_salary_slips(employees, args):
|
def get_existing_salary_slips(employees, args):
|
||||||
return frappe.db.sql_list("""
|
return frappe.db.sql_list("""
|
||||||
select distinct employee from `tabSalary Slip`
|
select distinct employee from `tabSalary Slip`
|
||||||
where docstatus!= 2 and company = %s
|
where docstatus!= 2 and company = %s and payroll_entry = %s
|
||||||
and start_date >= %s and end_date <= %s
|
and start_date >= %s and end_date <= %s
|
||||||
and employee in (%s)
|
and employee in (%s)
|
||||||
""" % ('%s', '%s', '%s', ', '.join(['%s']*len(employees))),
|
""" % ('%s', '%s', '%s', '%s', ', '.join(['%s']*len(employees))),
|
||||||
[args.company, args.start_date, args.end_date] + employees)
|
[args.company, args.payroll_entry, args.start_date, args.end_date] + employees)
|
||||||
|
|
||||||
def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
|
def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
|
||||||
submitted_ss = []
|
submitted_ss = []
|
||||||
@ -644,3 +638,61 @@ def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filte
|
|||||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||||
'start': start, 'page_len': page_len
|
'start': start, 'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def get_employee_with_existing_salary_slip(start_date, end_date, company):
|
||||||
|
return frappe.db.sql_list("""
|
||||||
|
select employee from `tabSalary Slip`
|
||||||
|
where
|
||||||
|
(start_date between %(start_date)s and %(end_date)s
|
||||||
|
or
|
||||||
|
end_date between %(start_date)s and %(end_date)s
|
||||||
|
or
|
||||||
|
%(start_date)s between start_date and end_date)
|
||||||
|
and company = %(company)s
|
||||||
|
and docstatus = 1
|
||||||
|
""", {'start_date': start_date, 'end_date': end_date, 'company': company})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
|
def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
filters = frappe._dict(filters)
|
||||||
|
conditions = []
|
||||||
|
exclude_employees = []
|
||||||
|
emp_cond = ''
|
||||||
|
if filters.start_date and filters.end_date:
|
||||||
|
employee_list = get_employee_with_existing_salary_slip(filters.start_date, filters.end_date, filters.company)
|
||||||
|
emp = filters.get('employees')
|
||||||
|
filters.pop('start_date')
|
||||||
|
filters.pop('end_date')
|
||||||
|
if filters.employees is not None:
|
||||||
|
filters.pop('employees')
|
||||||
|
if employee_list:
|
||||||
|
exclude_employees.extend(employee_list)
|
||||||
|
if emp:
|
||||||
|
exclude_employees.extend(emp)
|
||||||
|
if exclude_employees:
|
||||||
|
emp_cond += 'and employee not in %(exclude_employees)s'
|
||||||
|
|
||||||
|
return frappe.db.sql("""select name, employee_name from `tabEmployee`
|
||||||
|
where status = 'Active'
|
||||||
|
and docstatus < 2
|
||||||
|
and ({key} like %(txt)s
|
||||||
|
or employee_name like %(txt)s)
|
||||||
|
{emp_cond}
|
||||||
|
{fcond} {mcond}
|
||||||
|
order by
|
||||||
|
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||||
|
if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
|
||||||
|
idx desc,
|
||||||
|
name, employee_name
|
||||||
|
limit %(start)s, %(page_len)s""".format(**{
|
||||||
|
'key': searchfield,
|
||||||
|
'fcond': get_filters_cond(doctype, filters, conditions),
|
||||||
|
'mcond': get_match_cond(doctype),
|
||||||
|
'emp_cond': emp_cond
|
||||||
|
}), {
|
||||||
|
'txt': "%%%s%%" % txt,
|
||||||
|
'_txt': txt.replace("%", ""),
|
||||||
|
'start': start,
|
||||||
|
'page_len': page_len,
|
||||||
|
'exclude_employees': exclude_employees})
|
||||||
|
@ -51,21 +51,22 @@ class TestPayrollEntry(unittest.TestCase):
|
|||||||
|
|
||||||
company_doc = frappe.get_doc('Company', company)
|
company_doc = frappe.get_doc('Company', company)
|
||||||
salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD')
|
salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD')
|
||||||
create_salary_structure_assignment(employee, salary_structure.name, company=company)
|
create_salary_structure_assignment(employee, salary_structure.name, company=company, currency='USD')
|
||||||
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"})))
|
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"})))
|
||||||
salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure")
|
salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure")
|
||||||
dates = get_start_end_dates('Monthly', nowdate())
|
dates = get_start_end_dates('Monthly', nowdate())
|
||||||
payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
|
payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
|
||||||
payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70)
|
payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70)
|
||||||
payroll_entry.make_payment_entry()
|
payroll_entry.make_payment_entry()
|
||||||
|
|
||||||
salary_slip.load_from_db()
|
salary_slip.load_from_db()
|
||||||
|
|
||||||
payroll_je = salary_slip.journal_entry
|
payroll_je = salary_slip.journal_entry
|
||||||
payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
|
if payroll_je:
|
||||||
|
payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
|
||||||
|
|
||||||
self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
|
self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
|
||||||
self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
|
self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
|
||||||
|
|
||||||
payment_entry = frappe.db.sql('''
|
payment_entry = frappe.db.sql('''
|
||||||
Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
|
Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 14:50:29.401020",
|
"modified": "2021-03-31 22:43:28.363644",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Retention Bonus",
|
"name": "Retention Bonus",
|
||||||
|
@ -39,7 +39,8 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
|
|
||||||
frm.set_query("employee", function() {
|
frm.set_query("employee", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.employee_query"
|
query: "erpnext.controllers.queries.employee_query",
|
||||||
|
filters: frm.doc.company
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -93,27 +94,31 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_exchange_rate: function(frm, company_currency) {
|
set_exchange_rate: function(frm, company_currency) {
|
||||||
if (frm.doc.currency) {
|
if (frm.doc.docstatus === 0) {
|
||||||
var from_currency = frm.doc.currency;
|
if (frm.doc.currency) {
|
||||||
if (from_currency != company_currency) {
|
var from_currency = frm.doc.currency;
|
||||||
frm.events.hide_loan_section(frm);
|
if (from_currency != company_currency) {
|
||||||
frappe.call({
|
frm.events.hide_loan_section(frm);
|
||||||
method: "erpnext.setup.utils.get_exchange_rate",
|
frappe.call({
|
||||||
args: {
|
method: "erpnext.setup.utils.get_exchange_rate",
|
||||||
from_currency: from_currency,
|
args: {
|
||||||
to_currency: company_currency,
|
from_currency: from_currency,
|
||||||
},
|
to_currency: company_currency,
|
||||||
callback: function(r) {
|
},
|
||||||
frm.set_value("exchange_rate", flt(r.message));
|
callback: function(r) {
|
||||||
frm.set_df_property("exchange_rate", "hidden", 0);
|
if (r.message) {
|
||||||
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
|
frm.set_value("exchange_rate", flt(r.message));
|
||||||
+ " = [?] " + company_currency);
|
frm.set_df_property('exchange_rate', 'hidden', 0);
|
||||||
}
|
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
|
||||||
});
|
+ " = [?] " + company_currency);
|
||||||
} else {
|
}
|
||||||
frm.set_value("exchange_rate", 1.0);
|
}
|
||||||
frm.set_df_property("exchange_rate", "hidden", 1);
|
});
|
||||||
frm.set_df_property("exchange_rate", "description", "");
|
} else {
|
||||||
|
frm.set_value("exchange_rate", 1.0);
|
||||||
|
frm.set_df_property('exchange_rate', 'hidden', 1);
|
||||||
|
frm.set_df_property("exchange_rate", "description", "" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -631,7 +631,7 @@
|
|||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 15:39:28.817166",
|
"modified": "2021-03-31 22:44:09.772331",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
@ -124,9 +124,12 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
def check_existing(self):
|
def check_existing(self):
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
|
cond = ""
|
||||||
|
if self.payroll_entry:
|
||||||
|
cond += "and payroll_entry = '{0}'".format(self.payroll_entry)
|
||||||
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
|
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
|
||||||
where start_date = %s and end_date = %s and docstatus != 2
|
where start_date = %s and end_date = %s and docstatus != 2
|
||||||
and employee = %s and name != %s""",
|
and employee = %s and name != %s {0}""".format(cond),
|
||||||
(self.start_date, self.end_date, self.employee, self.name))
|
(self.start_date, self.end_date, self.employee, self.name))
|
||||||
if ret_exist:
|
if ret_exist:
|
||||||
self.employee = ''
|
self.employee = ''
|
||||||
@ -595,10 +598,10 @@ class SalarySlip(TransactionBase):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not d.additional_salary
|
(not d.additional_salary
|
||||||
and (not additional_salary or additional_salary.overwrite)
|
and (not additional_salary or additional_salary.overwrite))
|
||||||
or additional_salary
|
or (additional_salary
|
||||||
and additional_salary.name == d.additional_salary
|
and additional_salary.name == d.additional_salary)
|
||||||
):
|
):
|
||||||
component_row = d
|
component_row = d
|
||||||
break
|
break
|
||||||
@ -608,7 +611,7 @@ class SalarySlip(TransactionBase):
|
|||||||
self.set(component_type, [
|
self.set(component_type, [
|
||||||
d for d in self.get(component_type)
|
d for d in self.get(component_type)
|
||||||
if d.salary_component != component_data.salary_component
|
if d.salary_component != component_data.salary_component
|
||||||
or d.additional_salary and additional_salary.name != d.additional_salary
|
or (d.additional_salary and additional_salary.name != d.additional_salary)
|
||||||
or d == component_row
|
or d == component_row
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -630,8 +633,6 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
if additional_salary:
|
if additional_salary:
|
||||||
component_row.default_amount = 0
|
component_row.default_amount = 0
|
||||||
component_row.additional_amount = amount
|
|
||||||
component_row.additional_salary = additional_salary.name
|
|
||||||
component_row.deduct_full_tax_on_selected_payroll_date = \
|
component_row.deduct_full_tax_on_selected_payroll_date = \
|
||||||
additional_salary.deduct_full_tax_on_selected_payroll_date
|
additional_salary.deduct_full_tax_on_selected_payroll_date
|
||||||
else:
|
else:
|
||||||
@ -1053,7 +1054,7 @@ class SalarySlip(TransactionBase):
|
|||||||
repayment_entry.save()
|
repayment_entry.save()
|
||||||
repayment_entry.submit()
|
repayment_entry.submit()
|
||||||
|
|
||||||
loan.loan_repayment_entry = repayment_entry.name
|
frappe.db.set_value("Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name)
|
||||||
|
|
||||||
def cancel_loan_repayment_entry(self):
|
def cancel_loan_repayment_entry(self):
|
||||||
for loan in self.loans:
|
for loan in self.loans:
|
||||||
|
@ -312,7 +312,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
|
frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
|
||||||
|
|
||||||
create_salary_slips_for_payroll_period(applicant, salary_structure.name,
|
create_salary_slips_for_payroll_period(applicant, salary_structure.name,
|
||||||
payroll_period, deduct_random=False)
|
payroll_period, deduct_random=False, num=6)
|
||||||
|
|
||||||
salary_slips = frappe.get_all('Salary Slip', fields=['year_to_date', 'net_pay'], filters={'employee_name':
|
salary_slips = frappe.get_all('Salary Slip', fields=['year_to_date', 'net_pay'], filters={'employee_name':
|
||||||
'test_ytd@salary.com'}, order_by = 'posting_date')
|
'test_ytd@salary.com'}, order_by = 'posting_date')
|
||||||
|
@ -111,12 +111,19 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
frappe.set_route('Form', 'Salary Structure Assignment', doc.name);
|
frappe.set_route('Form', 'Salary Structure Assignment', doc.name);
|
||||||
});
|
});
|
||||||
frm.add_custom_button(__("Assign to Employees"),function () {
|
frm.add_custom_button(__("Assign to Employees"),function () {
|
||||||
frm.trigger('assign_to_employees')
|
frm.trigger('assign_to_employees')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set columns read-only
|
||||||
let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"];
|
let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"];
|
||||||
fields_read_only.forEach(function(field) {
|
fields_read_only.forEach(function(field) {
|
||||||
frappe.meta.get_docfield("Salary Detail", field, frm.doc.name).read_only = 1;
|
frm.fields_dict.earnings.grid.update_docfield_property(
|
||||||
|
field, 'read_only', 1
|
||||||
|
);
|
||||||
|
frm.fields_dict.deductions.grid.update_docfield_property(
|
||||||
|
field, 'read_only', 1
|
||||||
|
);
|
||||||
});
|
});
|
||||||
frm.trigger('set_earning_deduction_component');
|
frm.trigger('set_earning_deduction_component');
|
||||||
},
|
},
|
||||||
|
@ -164,7 +164,13 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non
|
|||||||
salary_structure_assignment.employee = employee
|
salary_structure_assignment.employee = employee
|
||||||
salary_structure_assignment.base = 50000
|
salary_structure_assignment.base = 50000
|
||||||
salary_structure_assignment.variable = 5000
|
salary_structure_assignment.variable = 5000
|
||||||
salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1)
|
|
||||||
|
if getdate(nowdate()).day == 1:
|
||||||
|
date = from_date or nowdate()
|
||||||
|
else:
|
||||||
|
date = from_date or add_days(nowdate(), -1)
|
||||||
|
|
||||||
|
salary_structure_assignment.from_date = date
|
||||||
salary_structure_assignment.salary_structure = salary_structure
|
salary_structure_assignment.salary_structure = salary_structure
|
||||||
salary_structure_assignment.currency = currency
|
salary_structure_assignment.currency = currency
|
||||||
salary_structure_assignment.payroll_payable_account = get_payable_account(company)
|
salary_structure_assignment.payroll_payable_account = get_payable_account(company)
|
||||||
|
@ -145,7 +145,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 15:49:36.361253",
|
"modified": "2021-03-31 22:44:46.267974",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Structure Assignment",
|
"name": "Salary Structure Assignment",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"is_mandatory": 1,
|
"is_mandatory": 1,
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2020-06-01 11:53:54.553947",
|
"modified": "2020-06-29 11:53:54.553947",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Create Payroll Period",
|
"name": "Create Payroll Period",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"action": "Go to Page",
|
"action": "Update Settings",
|
||||||
"creation": "2020-06-04 16:34:29.664917",
|
"creation": "2020-06-04 16:34:29.664917",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Onboarding Step",
|
"doctype": "Onboarding Step",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_mandatory": 0,
|
"is_mandatory": 0,
|
||||||
"is_single": 0,
|
"is_single": 1,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2020-06-04 16:34:29.664917",
|
"modified": "2020-06-29 16:34:29.664917",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Payroll Settings",
|
"name": "Payroll Settings",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"path": "#Form/Payroll Settings",
|
"reference_document": "Payroll Settings",
|
||||||
"show_full_form": 0,
|
"show_full_form": 0,
|
||||||
"title": "Payroll Settings",
|
"title": "Payroll Settings",
|
||||||
"validate_action": 1
|
"validate_action": 0
|
||||||
}
|
}
|
@ -10,10 +10,12 @@ frappe.ui.form.on('Products Settings', {
|
|||||||
df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
||||||
).map(df => ({ label: df.label, value: df.fieldname }));
|
).map(df => ({ label: df.label, value: df.fieldname }));
|
||||||
|
|
||||||
const field = frappe.meta.get_docfield("Website Filter Field", "fieldname", frm.docname);
|
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
||||||
field.fieldtype = 'Select';
|
'fieldname', 'fieldtype', 'Select'
|
||||||
field.options = valid_fields;
|
);
|
||||||
frm.fields_dict.filter_fields.grid.refresh();
|
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
||||||
|
'fieldname', 'options', valid_fields
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -33,12 +33,16 @@ class TestProject(unittest.TestCase):
|
|||||||
|
|
||||||
def test_project_template_having_parent_child_tasks(self):
|
def test_project_template_having_parent_child_tasks(self):
|
||||||
project_name = "Test Project with Template - Tasks with Parent-Child Relation"
|
project_name = "Test Project with Template - Tasks with Parent-Child Relation"
|
||||||
|
|
||||||
|
if frappe.db.get_value('Project', {'project_name': project_name}, 'name'):
|
||||||
|
project_name = frappe.db.get_value('Project', {'project_name': project_name}, 'name')
|
||||||
|
|
||||||
frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
|
frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
|
||||||
frappe.delete_doc('Project', project_name)
|
frappe.delete_doc('Project', project_name)
|
||||||
|
|
||||||
task1 = task_exists("Test Template Task Parent")
|
task1 = task_exists("Test Template Task Parent")
|
||||||
if not task1:
|
if not task1:
|
||||||
task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=4)
|
task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=10)
|
||||||
|
|
||||||
task2 = task_exists("Test Template Task Child 1")
|
task2 = task_exists("Test Template Task Child 1")
|
||||||
if not task2:
|
if not task2:
|
||||||
@ -53,7 +57,7 @@ class TestProject(unittest.TestCase):
|
|||||||
tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
|
tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
|
||||||
|
|
||||||
self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
|
self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
|
||||||
self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 4))
|
self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 10))
|
||||||
|
|
||||||
self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
|
self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
|
||||||
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
|
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
|
||||||
|
@ -276,74 +276,3 @@ erpnext.taxes.set_conditional_mandatory_rate_or_amount = function(grid_row) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// For customizing print
|
|
||||||
cur_frm.pformat.total = function(doc) { return ''; }
|
|
||||||
cur_frm.pformat.discount_amount = function(doc) { return ''; }
|
|
||||||
cur_frm.pformat.grand_total = function(doc) { return ''; }
|
|
||||||
cur_frm.pformat.rounded_total = function(doc) { return ''; }
|
|
||||||
cur_frm.pformat.in_words = function(doc) { return ''; }
|
|
||||||
|
|
||||||
cur_frm.pformat.taxes= function(doc){
|
|
||||||
//function to make row of table
|
|
||||||
var make_row = function(title, val, bold, is_negative) {
|
|
||||||
var bstart = '<b>'; var bend = '</b>';
|
|
||||||
return '<tr><td style="width:50%;">' + (bold?bstart:'') + title + (bold?bend:'') + '</td>'
|
|
||||||
+ '<td style="width:50%;text-align:right;">' + (is_negative ? '- ' : '')
|
|
||||||
+ format_currency(val, doc.currency) + '</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function print_hide(fieldname) {
|
|
||||||
var doc_field = frappe.meta.get_docfield(doc.doctype, fieldname, doc.name);
|
|
||||||
return doc_field.print_hide;
|
|
||||||
}
|
|
||||||
|
|
||||||
out ='';
|
|
||||||
if (!doc.print_without_amount) {
|
|
||||||
var cl = doc.taxes || [];
|
|
||||||
|
|
||||||
// outer table
|
|
||||||
var out='<div><table class="noborder" style="width:100%"><tr><td style="width: 60%"></td><td>';
|
|
||||||
|
|
||||||
// main table
|
|
||||||
|
|
||||||
out +='<table class="noborder" style="width:100%">';
|
|
||||||
|
|
||||||
if(!print_hide('total')) {
|
|
||||||
out += make_row('Total', doc.total, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discount Amount on net total
|
|
||||||
if(!print_hide('discount_amount') && doc.apply_discount_on == "Net Total" && doc.discount_amount)
|
|
||||||
out += make_row('Discount Amount', doc.discount_amount, 0, 1);
|
|
||||||
|
|
||||||
// add rows
|
|
||||||
if(cl.length){
|
|
||||||
for(var i=0;i<cl.length;i++) {
|
|
||||||
if(cl[i].tax_amount!=0 && !cl[i].included_in_print_rate)
|
|
||||||
out += make_row(cl[i].description, cl[i].tax_amount, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discount Amount on grand total
|
|
||||||
if(!print_hide('discount_amount') && doc.apply_discount_on == "Grand Total" && doc.discount_amount)
|
|
||||||
out += make_row('Discount Amount', doc.discount_amount, 0, 1);
|
|
||||||
|
|
||||||
// grand total
|
|
||||||
if(!print_hide('grand_total'))
|
|
||||||
out += make_row('Grand Total', doc.grand_total, 1);
|
|
||||||
|
|
||||||
if(!print_hide('rounded_total'))
|
|
||||||
out += make_row('Rounded Total', doc.rounded_total, 1);
|
|
||||||
|
|
||||||
if(doc.in_words && !print_hide('in_words')) {
|
|
||||||
out +='</table></td></tr>';
|
|
||||||
out += '<tr><td colspan = "2">';
|
|
||||||
out += '<table><tr><td style="width:25%;"><b>In Words</b></td>';
|
|
||||||
out += '<td style="width:50%;">' + doc.in_words + '</td></tr>';
|
|
||||||
}
|
|
||||||
out += '</table></td></tr></table></div>';
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
@ -323,12 +323,15 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
// set precision in the last item iteration
|
// set precision in the last item iteration
|
||||||
if (n == me.frm.doc["items"].length - 1) {
|
if (n == me.frm.doc["items"].length - 1) {
|
||||||
me.round_off_totals(tax);
|
me.round_off_totals(tax);
|
||||||
|
me.set_in_company_currency(tax,
|
||||||
|
["tax_amount", "tax_amount_after_discount_amount"]);
|
||||||
|
|
||||||
|
me.round_off_base_values(tax);
|
||||||
|
|
||||||
// in tax.total, accumulate grand total for each item
|
// in tax.total, accumulate grand total for each item
|
||||||
me.set_cumulative_total(i, tax);
|
me.set_cumulative_total(i, tax);
|
||||||
|
|
||||||
me.set_in_company_currency(tax,
|
me.set_in_company_currency(tax, ["total"]);
|
||||||
["total", "tax_amount", "tax_amount_after_discount_amount"]);
|
|
||||||
|
|
||||||
// adjust Discount Amount loss in last tax iteration
|
// adjust Discount Amount loss in last tax iteration
|
||||||
if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied
|
if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied
|
||||||
@ -393,20 +396,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
current_tax_amount = tax_rate * item.qty;
|
current_tax_amount = tax_rate * item.qty;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_tax_amount = this.get_final_tax_amount(tax, current_tax_amount);
|
|
||||||
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
|
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
|
||||||
|
|
||||||
return current_tax_amount;
|
return current_tax_amount;
|
||||||
},
|
},
|
||||||
|
|
||||||
get_final_tax_amount: function(tax, current_tax_amount) {
|
|
||||||
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
|
|
||||||
current_tax_amount = Math.round(current_tax_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return current_tax_amount;
|
|
||||||
},
|
|
||||||
|
|
||||||
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
|
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
|
||||||
// store tax breakup for each item
|
// store tax breakup for each item
|
||||||
let tax_detail = tax.item_wise_tax_detail;
|
let tax_detail = tax.item_wise_tax_detail;
|
||||||
@ -420,10 +414,22 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
round_off_totals: function(tax) {
|
round_off_totals: function(tax) {
|
||||||
|
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
|
||||||
|
tax.tax_amount= Math.round(tax.tax_amount);
|
||||||
|
tax.tax_amount_after_discount_amount = Math.round(tax.tax_amount_after_discount_amount);
|
||||||
|
}
|
||||||
|
|
||||||
tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
|
tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
|
||||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
|
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
round_off_base_values: function(tax) {
|
||||||
|
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
|
||||||
|
tax.base_tax_amount= Math.round(tax.base_tax_amount);
|
||||||
|
tax.base_tax_amount_after_discount_amount = Math.round(tax.base_tax_amount_after_discount_amount);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
manipulate_grand_total_for_inclusive_tax: function() {
|
manipulate_grand_total_for_inclusive_tax: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
// if fully inclusive taxes and diff
|
// if fully inclusive taxes and diff
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user