Merge branch 'develop' into employee_tree

This commit is contained in:
Marica 2020-07-08 14:04:49 +05:30 committed by GitHub
commit 3f05a2692e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 1194 additions and 567 deletions

View File

@ -6,6 +6,18 @@ frappe.ui.form.on('Item Tax Template', {
frm.set_query("tax_type", "taxes", function(doc) { frm.set_query("tax_type", "taxes", function(doc) {
return { return {
filters: [ filters: [
['Account', 'company', '=', frm.doc.company],
['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']]
]
}
});
},
company: function (frm) {
frm.set_query("tax_type", "taxes", function(doc) {
return {
filters: [
['Account', 'company', '=', frm.doc.company],
['Account', 'is_group', '=', 0], ['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']]
] ]

View File

@ -1,168 +1,85 @@
{ {
"allow_copy": 0, "allow_import": 1,
"allow_events_in_timeline": 0, "allow_rename": 1,
"allow_guest_to_view": 0, "autoname": "field:title",
"allow_import": 1, "creation": "2018-11-22 22:45:00.370913",
"allow_rename": 1, "doctype": "DocType",
"autoname": "field:title", "document_type": "Setup",
"beta": 0, "editable_grid": 1,
"creation": "2018-11-22 22:45:00.370913", "engine": "InnoDB",
"custom": 0, "field_order": [
"docstatus": 0, "title",
"doctype": "DocType", "company",
"document_type": "Setup", "taxes"
"editable_grid": 1, ],
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "title",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "in_filter": 1,
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Title",
"columns": 0, "no_copy": 1,
"fieldname": "title", "reqd": 1,
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0, "fieldname": "taxes",
"allow_in_quick_entry": 0, "fieldtype": "Table",
"allow_on_submit": 0, "label": "Tax Rates",
"bold": 0, "options": "Item Tax Template Detail",
"collapsible": 0, "reqd": 1
"columns": 0, },
"fieldname": "taxes", {
"fieldtype": "Table", "fieldname": "company",
"hidden": 0, "fieldtype": "Link",
"ignore_user_permissions": 0, "label": "Company",
"ignore_xss_filter": 0, "options": "Company",
"in_filter": 0, "reqd": 1
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tax Rates",
"length": 0,
"no_copy": 0,
"options": "Item Tax Template Detail",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "modified": "2020-06-18 20:27:42.615842",
"hide_heading": 0, "modified_by": "ahmad@havenir.com",
"hide_toolbar": 0, "module": "Accounts",
"idx": 0, "name": "Item Tax Template",
"image_view": 0, "owner": "Administrator",
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-12-21 23:51:16.328340",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template",
"name_case": "",
"owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"cancel": 0, "delete": 1,
"create": 1, "email": 1,
"delete": 1, "export": 1,
"email": 1, "print": 1,
"export": 1, "read": 1,
"if_owner": 0, "report": 1,
"import": 0, "role": "System Manager",
"permlevel": 0, "share": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "create": 1,
"cancel": 0, "delete": 1,
"create": 1, "email": 1,
"delete": 1, "export": 1,
"email": 1, "print": 1,
"export": 1, "read": 1,
"if_owner": 0, "report": 1,
"import": 0, "role": "Accounts Manager",
"permlevel": 0, "share": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "email": 1,
"cancel": 0, "export": 1,
"create": 0, "print": 1,
"delete": 0, "read": 1,
"email": 1, "report": 1,
"export": 1, "role": "Accounts User",
"if_owner": 0, "share": 1
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
} }
], ],
"quick_entry": 0, "show_name_in_global_search": 1,
"read_only": 0, "sort_field": "modified",
"read_only_onload": 0, "sort_order": "DESC",
"search_fields": "", "track_changes": 1
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
} }

View File

@ -840,6 +840,7 @@ def get_opening_accounts(company):
return [{"account": a, "balance": get_balance_on(a)} for a in accounts] return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
@frappe.whitelist()
def get_against_jv(doctype, txt, searchfield, start, page_len, filters): def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail

View File

@ -26,6 +26,7 @@ class PaymentOrder(Document):
for d in self.references: for d in self.references:
frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status) frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
@frappe.whitelist()
def get_mop_query(doctype, txt, searchfield, start, page_len, filters): def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference` return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference`
where parent = %(parent)s and mode_of_payment like %(txt)s where parent = %(parent)s and mode_of_payment like %(txt)s
@ -36,6 +37,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
'txt': "%%%s%%" % txt 'txt': "%%%s%%" % txt
}) })
@frappe.whitelist()
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters): def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" select supplier from `tabPayment Order Reference` return frappe.db.sql(""" select supplier from `tabPayment Order Reference`
where parent = %(parent)s and supplier like %(txt)s and where parent = %(parent)s and supplier like %(txt)s and
@ -86,4 +88,4 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
je.flags.ignore_mandatory = True je.flags.ignore_mandatory = True
je.save() je.save()
frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name))

View File

@ -101,10 +101,10 @@ class PaymentReconciliation(Document):
Having Having
amount > 0 amount > 0
""".format( """.format(
doc=voucher_type, doc=voucher_type,
dr_or_cr=dr_or_cr, dr_or_cr=dr_or_cr,
reconciled_dr_or_cr=reconciled_dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr,
party_type_field=frappe.scrub(self.party_type)), party_type_field=frappe.scrub(self.party_type)),
{ {
'party': self.party, 'party': self.party,
'party_type': self.party_type, 'party_type': self.party_type,
@ -170,7 +170,7 @@ class PaymentReconciliation(Document):
reconcile_against_document(lst) reconcile_against_document(lst)
if dr_or_cr_notes: if dr_or_cr_notes:
reconcile_dr_cr_note(dr_or_cr_notes) reconcile_dr_cr_note(dr_or_cr_notes, self.company)
msgprint(_("Successfully Reconciled")) msgprint(_("Successfully Reconciled"))
self.get_unreconciled_entries() self.get_unreconciled_entries()
@ -261,7 +261,7 @@ class PaymentReconciliation(Document):
return cond return cond
def reconcile_dr_cr_note(dr_cr_notes): def reconcile_dr_cr_note(dr_cr_notes, company):
for d in dr_cr_notes: for d in dr_cr_notes:
voucher_type = ('Credit Note' voucher_type = ('Credit Note'
if d.voucher_type == 'Sales Invoice' else 'Debit Note') if d.voucher_type == 'Sales Invoice' else 'Debit Note')
@ -273,6 +273,7 @@ def reconcile_dr_cr_note(dr_cr_notes):
"doctype": "Journal Entry", "doctype": "Journal Entry",
"voucher_type": voucher_type, "voucher_type": voucher_type,
"posting_date": today(), "posting_date": today(),
"company": company,
"accounts": [ "accounts": [
{ {
'account': d.account, 'account': d.account,

View File

@ -115,6 +115,7 @@ def get_item_groups(pos_profile):
def get_series(): def get_series():
return frappe.get_meta("Sales Invoice").get_field("naming_series").options or "" return frappe.get_meta("Sales Invoice").get_field("naming_series").options or ""
@frappe.whitelist()
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters): def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
user = frappe.session['user'] user = frappe.session['user']
company = filters.get('company') or frappe.defaults.get_user_default('company') company = filters.get('company') or frappe.defaults.get_user_default('company')

View File

@ -432,6 +432,7 @@ def make_pricing_rule(doctype, docname):
return doc return doc
@frappe.whitelist()
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
items = [filters.get('value')] items = [filters.get('value')]
if filters.get('apply_on') != 'Item Code': if filters.get('apply_on') != 'Item Code':
@ -442,4 +443,4 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
return frappe.get_all('UOM Conversion Detail', return frappe.get_all('UOM Conversion Detail',
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))}, filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
fields = ["distinct uom"], as_list=1) fields = ["distinct uom"], as_list=1)

File diff suppressed because it is too large Load Diff

View File

@ -140,10 +140,8 @@ def make_entry(args, adv_adj, update_outstanding):
gle = frappe.new_doc("GL Entry") gle = frappe.new_doc("GL Entry")
gle.update(args) gle.update(args)
gle.flags.ignore_permissions = 1 gle.flags.ignore_permissions = 1
gle.validate() gle.insert()
gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding) gle.run_method("on_update_with_args", adv_adj, update_outstanding)
gle.flags.ignore_validate = True
gle.submit() gle.submit()
# check against budget # check against budget

View File

@ -21,7 +21,7 @@ def reconcile(bank_transaction, payment_doctype, payment_name):
if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
frappe.throw(_("The unallocated amount of Payment Entry {0} \ frappe.throw(_("The unallocated amount of Payment Entry {0} \
is greater than the Bank Transaction's unallocated amount").format(payment_name)) is greater than the Bank Transaction's unallocated amount").format(payment_name))
if transaction.unallocated_amount == 0: if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled")) frappe.throw(_("This bank transaction is already fully reconciled"))
@ -289,6 +289,7 @@ def get_matching_transactions_payments(description_matching):
else: else:
return [] return []
@frappe.whitelist()
def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
if not account: if not account:
@ -317,6 +318,7 @@ def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
} }
) )
@frappe.whitelist()
def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
@ -352,6 +354,7 @@ def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
} }
) )
@frappe.whitelist()
def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" return frappe.db.sql("""
SELECT SELECT

View File

@ -388,7 +388,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
args = { args = {
party_type.lower(): party, party_type.lower(): party,
"company": company "company": company
} }
if tax_category: if tax_category:

View File

@ -133,7 +133,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
acc = frappe.get_doc("Account", account) acc = frappe.get_doc("Account", account)
try: try:
year_start_date = get_fiscal_year(date, verbose=0)[1] year_start_date = get_fiscal_year(date, company=company, verbose=0)[1]
except FiscalYearError: except FiscalYearError:
if getdate(date) > getdate(nowdate()): if getdate(date) > getdate(nowdate()):
# if fiscal year not found and the date is greater than today # if fiscal year not found and the date is greater than today
@ -787,10 +787,10 @@ def get_children(doctype, parent, company, is_root=False):
company_currency = frappe.get_cached_value('Company', company, "default_currency") company_currency = frappe.get_cached_value('Company', company, "default_currency")
for each in acc: for each in acc:
each["company_currency"] = company_currency each["company_currency"] = company_currency
each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False)) each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company))
if each.account_currency != company_currency: if each.account_currency != company_currency:
each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"))) each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company))
return acc return acc

View File

@ -58,7 +58,7 @@
"type": "Report" "type": "Report"
}, },
{ {
"label": "Assets Dashboard", "label": "Dashboard",
"link_to": "Asset", "link_to": "Asset",
"type": "Dashboard" "type": "Dashboard"
} }

View File

@ -35,11 +35,9 @@ class Asset(AccountsController):
if not self.booked_fixed_asset and self.validate_make_gl_entry(): if not self.booked_fixed_asset and self.validate_make_gl_entry():
self.make_gl_entries() self.make_gl_entries()
def before_cancel(self):
self.cancel_auto_gen_movement()
def on_cancel(self): def on_cancel(self):
self.validate_cancellation() self.validate_cancellation()
self.cancel_movement_entries()
self.delete_depreciation_entries() self.delete_depreciation_entries()
self.set_status() self.set_status()
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
@ -134,19 +132,6 @@ class Asset(AccountsController):
Please do not book expense of multiple assets against one single Asset.") Please do not book expense of multiple assets against one single Asset.")
.format(frappe.bold("equal"), "<br>"), title=_("Invalid Gross Purchase Amount")) .format(frappe.bold("equal"), "<br>"), title=_("Invalid Gross Purchase Amount"))
def cancel_auto_gen_movement(self):
movements = frappe.db.sql(
"""SELECT asm.name, asm.docstatus
FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1)
if len(movements) > 1:
frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \
cancelled manually to cancel this asset.'))
if movements:
movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
movement.flags.ignore_validate = True
movement.cancel()
def make_asset_movement(self): def make_asset_movement(self):
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
reference_docname = self.purchase_receipt or self.purchase_invoice reference_docname = self.purchase_receipt or self.purchase_invoice
@ -413,6 +398,16 @@ class Asset(AccountsController):
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
def cancel_movement_entries(self):
movements = frappe.db.sql(
"""SELECT asm.name, asm.docstatus
FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1)
for movement in movements:
movement = frappe.get_doc('Asset Movement', movement.get('name'))
movement.cancel()
def delete_depreciation_entries(self): def delete_depreciation_entries(self):
for d in self.get("schedules"): for d in self.get("schedules"):
if d.journal_entry: if d.journal_entry:

View File

@ -87,33 +87,9 @@ class AssetMovement(Document):
def on_submit(self): def on_submit(self):
self.set_latest_location_in_asset() self.set_latest_location_in_asset()
def before_cancel(self):
self.validate_last_movement()
def on_cancel(self): def on_cancel(self):
self.set_latest_location_in_asset() self.set_latest_location_in_asset()
def validate_last_movement(self):
for d in self.assets:
auto_gen_movement_entry = frappe.db.sql(
"""
SELECT asm.name
FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
WHERE
asm.docstatus=1 and
asm_item.parent=asm.name and
asm_item.asset=%s and
asm.company=%s and
asm_item.source_location is NULL and
asm.purpose=%s
ORDER BY
asm.transaction_date asc
""", (d.asset, self.company, 'Receipt'), as_dict=1)
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
auto generated for Asset {1}').format(self.name, d.asset))
def set_latest_location_in_asset(self): def set_latest_location_in_asset(self):
current_location, current_employee = '', '' current_location, current_employee = '', ''

View File

@ -33,7 +33,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Other Reports", "label": "Other Reports",
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
}, },
{ {
"hidden": 0, "hidden": 0,
@ -60,7 +60,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Buying", "label": "Buying",
"modified": "2020-05-28 13:32:49.960574", "modified": "2020-06-29 19:30:24.983050",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying", "name": "Buying",

View File

@ -166,7 +166,8 @@ frappe.ui.form.on("Request for Quotation",{
{ "fieldtype": "Select", "label": __("Supplier"), { "fieldtype": "Select", "label": __("Supplier"),
"fieldname": "supplier", "fieldname": "supplier",
"options": doc.suppliers.map(d => d.supplier), "options": doc.suppliers.map(d => d.supplier),
"reqd": 1 }, "reqd": 1,
"default": doc.suppliers.length === 1 ? doc.suppliers[0].supplier_name : "" },
{ "fieldtype": "Button", "label": __('Create Supplier Quotation'), { "fieldtype": "Button", "label": __('Create Supplier Quotation'),
"fieldname": "make_supplier_quotation", "cssClass": "btn-primary" }, "fieldname": "make_supplier_quotation", "cssClass": "btn-primary" },
] ]

View File

@ -206,6 +206,7 @@ def get_list_context(context=None):
}) })
return list_context return list_context
@frappe.whitelist()
def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters): def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select `tabContact`.name from `tabContact`, `tabDynamic Link` return frappe.db.sql("""select `tabContact`.name from `tabContact`, `tabDynamic Link`
where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s

View File

@ -10,7 +10,8 @@ from collections import defaultdict
from erpnext.stock.get_item_details import _get_item_tax_template from erpnext.stock.get_item_details import _get_item_tax_template
from frappe.utils import unique from frappe.utils import unique
# searches for active employees # searches for active employees
@frappe.whitelist()
def employee_query(doctype, txt, searchfield, start, page_len, filters): def employee_query(doctype, txt, searchfield, start, page_len, filters):
conditions = [] conditions = []
fields = get_fields("Employee", ["name", "employee_name"]) fields = get_fields("Employee", ["name", "employee_name"])
@ -40,6 +41,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
# searches for leads which are not converted # searches for leads which are not converted
@frappe.whitelist()
def lead_query(doctype, txt, searchfield, start, page_len, filters): def lead_query(doctype, txt, searchfield, start, page_len, filters):
fields = get_fields("Lead", ["name", "lead_name", "company_name"]) fields = get_fields("Lead", ["name", "lead_name", "company_name"])
@ -69,6 +71,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
# searches for customer # searches for customer
@frappe.whitelist()
def customer_query(doctype, txt, searchfield, start, page_len, filters): def customer_query(doctype, txt, searchfield, start, page_len, filters):
conditions = [] conditions = []
cust_master_name = frappe.defaults.get_user_default("cust_master_name") cust_master_name = frappe.defaults.get_user_default("cust_master_name")
@ -106,6 +109,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
# searches for supplier # searches for supplier
@frappe.whitelist()
def supplier_query(doctype, txt, searchfield, start, page_len, filters): def supplier_query(doctype, txt, searchfield, start, page_len, filters):
supp_master_name = frappe.defaults.get_user_default("supp_master_name") supp_master_name = frappe.defaults.get_user_default("supp_master_name")
if supp_master_name == "Supplier Name": if supp_master_name == "Supplier Name":
@ -137,6 +141,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
}) })
@frappe.whitelist()
def tax_account_query(doctype, txt, searchfield, start, page_len, filters): def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
company_currency = erpnext.get_company_currency(filters.get('company')) company_currency = erpnext.get_company_currency(filters.get('company'))
@ -162,6 +167,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
return tax_accounts return tax_accounts
@frappe.whitelist()
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
conditions = [] conditions = []
@ -224,6 +230,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
}, as_dict=as_dict) }, as_dict=as_dict)
@frappe.whitelist()
def bom(doctype, txt, searchfield, start, page_len, filters): def bom(doctype, txt, searchfield, start, page_len, filters):
conditions = [] conditions = []
fields = get_fields("BOM", ["name", "item"]) fields = get_fields("BOM", ["name", "item"])
@ -250,6 +257,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
}) })
@frappe.whitelist()
def get_project_name(doctype, txt, searchfield, start, page_len, filters): def get_project_name(doctype, txt, searchfield, start, page_len, filters):
cond = '' cond = ''
if filters.get('customer'): if filters.get('customer'):
@ -276,6 +284,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
}) })
@frappe.whitelist()
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) fields = get_fields("Delivery Note", ["name", "customer", "posting_date"])
@ -305,6 +314,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict) }, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
@frappe.whitelist()
def get_batch_no(doctype, txt, searchfield, start, page_len, filters): def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
cond = "" cond = ""
if filters.get("posting_date"): if filters.get("posting_date"):
@ -362,6 +372,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
@frappe.whitelist()
def get_account_list(doctype, txt, searchfield, start, page_len, filters): def get_account_list(doctype, txt, searchfield, start, page_len, filters):
filter_list = [] filter_list = []
@ -385,6 +396,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
limit_start=start, limit_page_length=page_len, as_list=True) limit_start=start, limit_page_length=page_len, as_list=True)
@frappe.whitelist()
def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date
from `tabBlanket Order` bo, `tabBlanket Order Item` boi from `tabBlanket Order` bo, `tabBlanket Order Item` boi
@ -570,7 +582,8 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
args = { args = {
'item_code': filters.get('item_code'), 'item_code': filters.get('item_code'),
'posting_date': filters.get('valid_from'), 'posting_date': filters.get('valid_from'),
'tax_category': filters.get('tax_category') 'tax_category': filters.get('tax_category'),
'company': filters.get('company')
} }
taxes = _get_item_tax_template(args, taxes, for_validate=True) taxes = _get_item_tax_template(args, taxes, for_validate=True)

View File

@ -53,7 +53,8 @@ class calculate_taxes_and_totals(object):
'tax_category': self.doc.get('tax_category'), 'tax_category': self.doc.get('tax_category'),
'posting_date': self.doc.get('posting_date'), 'posting_date': self.doc.get('posting_date'),
'bill_date': self.doc.get('bill_date'), 'bill_date': self.doc.get('bill_date'),
'transaction_date': self.doc.get('transaction_date') 'transaction_date': self.doc.get('transaction_date'),
'company': self.doc.get('company')
} }
item_group = item_doc.item_group item_group = item_doc.item_group

View File

@ -172,7 +172,9 @@ def get_number_cards():
"doctype": "Number Card", "doctype": "Number Card",
"document_type": "Lead", "document_type": "Lead",
"name": "New Lead (Last 1 Month)", "name": "New Lead (Last 1 Month)",
"filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]), "filters_json": json.dumps([
["Lead", "creation", "Timespan", "last month"]
]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("New Lead (Last 1 Month)"), "label": _("New Lead (Last 1 Month)"),
@ -183,7 +185,9 @@ def get_number_cards():
"doctype": "Number Card", "doctype": "Number Card",
"document_type": "Opportunity", "document_type": "Opportunity",
"name": "New Opportunity (Last 1 Month)", "name": "New Opportunity (Last 1 Month)",
"filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), "filters_json": json.dumps([
["Opportunity", "creation", "Timespan", "last month"]
]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("New Opportunity (Last 1 Month)"), "label": _("New Opportunity (Last 1 Month)"),
@ -194,7 +198,10 @@ def get_number_cards():
"doctype": "Number Card", "doctype": "Number Card",
"document_type": "Opportunity", "document_type": "Opportunity",
"name": "Won Opportunity (Last 1 Month)", "name": "Won Opportunity (Last 1 Month)",
"filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), "filters_json": json.dumps([
["Opportunity", "status", "=", "Converted",False],
["Opportunity", "creation", "Timespan", "last month"]
]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("Won Opportunity (Last 1 Month)"), "label": _("Won Opportunity (Last 1 Month)"),

View File

@ -114,10 +114,12 @@ class Lead(SellingController):
def set_lead_name(self): def set_lead_name(self):
if not self.lead_name: if not self.lead_name:
# Check for leads being created through data import # Check for leads being created through data import
if not self.company_name and not self.flags.ignore_mandatory: if not self.company_name and not self.email_id and not self.flags.ignore_mandatory:
frappe.throw(_("A Lead requires either a person's name or an organization's name")) frappe.throw(_("A Lead requires either a person's name or an organization's name"))
elif self.company_name:
self.lead_name = self.company_name self.lead_name = self.company_name
else:
self.lead_name = self.email_id.split("@")[0]
def set_title(self): def set_title(self):
if self.organization_lead: if self.organization_lead:

View File

@ -13,7 +13,7 @@ source_link = "https://github.com/frappe/erpnext"
app_logo_url = '/assets/erpnext/images/erp-icon.svg' app_logo_url = '/assets/erpnext/images/erp-icon.svg'
develop_version = '12.x.x-develop' develop_version = '13.x.x-develop'
app_include_js = "assets/js/erpnext.min.js" app_include_js = "assets/js/erpnext.min.js"
app_include_css = "assets/css/erpnext.css" app_include_css = "assets/css/erpnext.css"

View File

@ -113,6 +113,14 @@ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){
cur_frm.cscript.calculate_total(doc,cdt,cdn); cur_frm.cscript.calculate_total(doc,cdt,cdn);
}; };
cur_frm.fields_dict['cost_center'].get_query = function(doc) {
return {
filters: {
"company": doc.company
}
}
};
erpnext.expense_claim = { erpnext.expense_claim = {
set_title: function(frm) { set_title: function(frm) {
if (!frm.doc.task) { if (!frm.doc.task) {
@ -300,6 +308,11 @@ frappe.ui.form.on("Expense Claim", {
cost_center: function(frm) { cost_center: function(frm) {
frm.events.set_child_cost_center(frm); frm.events.set_child_cost_center(frm);
}, },
validate: function(frm) {
frm.events.set_child_cost_center(frm);
},
set_child_cost_center: function(frm){ set_child_cost_center: function(frm){
(frm.doc.expenses || []).forEach(function(d) { (frm.doc.expenses || []).forEach(function(d) {
if (!d.cost_center){ if (!d.cost_center){
@ -349,9 +362,6 @@ frappe.ui.form.on("Expense Claim", {
}); });
frappe.ui.form.on("Expense Claim Detail", { frappe.ui.form.on("Expense Claim Detail", {
expenses_add: function(frm, cdt, cdn) {
frm.events.set_child_cost_center(frm);
},
amount: function(frm, cdt, cdn) { amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount);

View File

@ -129,7 +129,7 @@ class ExpenseClaim(AccountsController):
"debit": data.sanctioned_amount, "debit": data.sanctioned_amount,
"debit_in_account_currency": data.sanctioned_amount, "debit_in_account_currency": data.sanctioned_amount,
"against": self.employee, "against": self.employee,
"cost_center": data.cost_center "cost_center": data.cost_center or self.cost_center
}, item=data) }, item=data)
) )

View File

@ -32,7 +32,8 @@ class JobOffer(Document):
return frappe.get_all("Job Offer", filters={ return frappe.get_all("Job Offer", filters={
"offer_date": ['between', (from_date, to_date)], "offer_date": ['between', (from_date, to_date)],
"designation": self.designation, "designation": self.designation,
"company": self.company "company": self.company,
"docstatus": 1
}, fields=['name']) }, fields=['name'])
def update_job_applicant(status, job_applicant): def update_job_applicant(status, job_applicant):

View File

@ -40,6 +40,8 @@ frappe.ui.form.on("Leave Application", {
validate: function(frm) { validate: function(frm) {
if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){
frm.doc.half_day_date = frm.doc.from_date; frm.doc.half_day_date = frm.doc.from_date;
}else if (frm.doc.half_day == 0){
frm.doc.half_day_date = "";
} }
frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
}, },

View File

@ -293,6 +293,8 @@ class LeaveApplication(Document):
def set_half_day_date(self): def set_half_day_date(self):
if self.from_date == self.to_date and self.half_day == 1: if self.from_date == self.to_date and self.half_day == 1:
self.half_day_date = self.from_date self.half_day_date = self.from_date
elif self.half_day == 0:
self.half_day_date = None
def notify_employee(self): def notify_employee(self):
employee = frappe.get_doc("Employee", self.employee) employee = frappe.get_doc("Employee", self.employee)

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from frappe import _
def get_data(): def get_data():
return { return {
@ -8,13 +9,17 @@ def get_data():
}, },
'transactions': [ 'transactions': [
{ {
'items': ['Employee'] 'label': _('Employees'),
}, 'items': ['Employee', 'Employee Grade']
{
'items': ['Employee Grade']
}, },
{ {
'label': _('Leaves'),
'items': ['Leave Allocation'] 'items': ['Leave Allocation']
}, },
] ]
} }

View File

@ -45,15 +45,6 @@ frappe.ui.form.on('Loan', {
}); });
}) })
frm.set_query('loan_security_pledge', function(doc, cdt, cdn) {
return {
filters: {
applicant: frm.doc.applicant,
docstatus: 1,
loan_application: frm.doc.loan_application || ''
}
};
});
}, },
refresh: function (frm) { refresh: function (frm) {
@ -86,9 +77,6 @@ frappe.ui.form.on('Loan', {
frm.toggle_display("repayment_periods", s1 - frm.doc.is_term_loan); frm.toggle_display("repayment_periods", s1 - frm.doc.is_term_loan);
}, },
is_secured_loan: function(frm) {
frm.toggle_reqd("loan_security_pledge", frm.doc.is_secured_loan);
},
make_loan_disbursement: function (frm) { make_loan_disbursement: function (frm) {
frappe.call({ frappe.call({

View File

@ -25,15 +25,12 @@
"disbursement_date", "disbursement_date",
"disbursed_amount", "disbursed_amount",
"column_break_11", "column_break_11",
"maximum_loan_amount",
"is_term_loan", "is_term_loan",
"repayment_method", "repayment_method",
"repayment_periods", "repayment_periods",
"monthly_repayment_amount", "monthly_repayment_amount",
"repayment_start_date", "repayment_start_date",
"loan_security_details_section",
"loan_security_pledge",
"column_break_25",
"maximum_loan_value",
"account_info", "account_info",
"mode_of_payment", "mode_of_payment",
"payment_account", "payment_account",
@ -292,13 +289,8 @@
"default": "0", "default": "0",
"fieldname": "is_secured_loan", "fieldname": "is_secured_loan",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Secured Loan" "label": "Is Secured Loan",
}, "read_only": 1
{
"depends_on": "is_secured_loan",
"fieldname": "loan_security_details_section",
"fieldtype": "Section Break",
"label": "Loan Security Details"
}, },
{ {
"default": "0", "default": "0",
@ -324,12 +316,6 @@
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
}, },
{
"fieldname": "loan_security_pledge",
"fieldtype": "Link",
"label": "Loan Security Pledge",
"options": "Loan Security Pledge"
},
{ {
"fieldname": "disbursed_amount", "fieldname": "disbursed_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
@ -338,21 +324,17 @@
"read_only": 1 "read_only": 1
}, },
{ {
"fetch_from": "loan_security_pledge.maximum_loan_value", "fetch_from": "loan_application.maximum_loan_amount",
"fieldname": "maximum_loan_value", "fieldname": "maximum_loan_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Maximum Loan Value", "label": "Maximum Loan Amount",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
},
{
"fieldname": "column_break_25",
"fieldtype": "Column Break"
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-13 13:16:10.192624", "modified": "2020-07-02 20:46:40.128142",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan", "name": "Loan",

View File

@ -13,11 +13,9 @@ from erpnext.controllers.accounts_controller import AccountsController
class Loan(AccountsController): class Loan(AccountsController):
def validate(self): def validate(self):
self.set_loan_amount() self.set_loan_amount()
self.validate_loan_amount()
self.set_missing_fields() self.set_missing_fields()
self.validate_accounts() self.validate_accounts()
self.validate_loan_security_pledge()
self.validate_loan_amount()
self.check_sanctioned_amount_limit() self.check_sanctioned_amount_limit()
self.validate_repay_from_salary() self.validate_repay_from_salary()
@ -56,21 +54,6 @@ class Loan(AccountsController):
if self.repayment_method == "Repay Over Number of Periods": if self.repayment_method == "Repay Over Number of Periods":
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
def validate_loan_security_pledge(self):
if self.is_secured_loan and not self.loan_security_pledge:
frappe.throw(_("Loan Security Pledge is mandatory for secured loan"))
if self.loan_security_pledge:
loan_security_details = frappe.db.get_value("Loan Security Pledge", self.loan_security_pledge,
['loan', 'company'], as_dict=1)
if loan_security_details.loan:
frappe.throw(_("Loan Security Pledge already pledged against loan {0}").format(loan_security_details.loan))
if loan_security_details.company != self.company:
frappe.throw(_("Loan Security Pledge Company and Loan Company must be same"))
def check_sanctioned_amount_limit(self): def check_sanctioned_amount_limit(self):
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company) sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
@ -129,22 +112,29 @@ class Loan(AccountsController):
self.total_payment = self.loan_amount self.total_payment = self.loan_amount
def set_loan_amount(self): def set_loan_amount(self):
if self.loan_application and not self.loan_amount:
self.loan_amount = frappe.db.get_value('Loan Application', self.loan_application, 'loan_amount')
if not self.loan_amount and self.is_secured_loan and self.loan_security_pledge:
self.loan_amount = self.maximum_loan_value
def validate_loan_amount(self): def validate_loan_amount(self):
if self.is_secured_loan and self.loan_amount > self.maximum_loan_value: if self.maximum_loan_amount and self.loan_amount > self.maximum_loan_amount:
msg = _("Loan amount cannot be greater than {0}").format(self.maximum_loan_value) msg = _("Loan amount cannot be greater than {0}").format(self.maximum_loan_amount)
frappe.throw(msg) frappe.throw(msg)
if not self.loan_amount: if not self.loan_amount:
frappe.throw(_("Loan amount is mandatory")) frappe.throw(_("Loan amount is mandatory"))
def link_loan_security_pledge(self): def link_loan_security_pledge(self):
frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET if self.is_secured_loan:
loan = %s, status = 'Pledged', pledge_time = %s loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application},
where name = %s """, (self.name, now_datetime(), self.loan_security_pledge)) 'name')
if loan_security_pledge:
frappe.db.set_value('Loan Security Pledge', loan_security_pledge, {
'loan': self.name,
'status': 'Pledged',
'pledge_time': now_datetime()
})
def unlink_loan_security_pledge(self): def unlink_loan_security_pledge(self):
frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET

View File

@ -16,6 +16,7 @@ from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual
from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall
from erpnext.loan_management.doctype.loan.loan import create_loan_security_unpledge from erpnext.loan_management.doctype.loan.loan import create_loan_security_unpledge
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
class TestLoan(unittest.TestCase): class TestLoan(unittest.TestCase):
def setUp(self): def setUp(self):
@ -72,31 +73,31 @@ class TestLoan(unittest.TestCase):
self.assertEquals(loan.total_payment, 302712) self.assertEquals(loan.total_payment, 302712)
def test_loan_with_security(self): def test_loan_with_security(self):
pledges = []
pledges.append({ pledge = [{
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00,
"haircut": 50, }]
"loan_security_price": 500.00
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2,
'Stock Loan', pledge, "Repay Over Number of Periods", 12)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) create_pledge(loan_application)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods",
12, loan_application)
self.assertEquals(loan.loan_amount, 1000000) self.assertEquals(loan.loan_amount, 1000000)
def test_loan_disbursement(self): def test_loan_disbursement(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50 }]
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) create_pledge(loan_application)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
self.assertEquals(loan.loan_amount, 1000000) self.assertEquals(loan.loan_amount, 1000000)
loan.submit() loan.submit()
@ -121,18 +122,15 @@ class TestLoan(unittest.TestCase):
self.assertTrue(gl_entries2) self.assertTrue(gl_entries2)
def test_regular_loan_repayment(self): def test_regular_loan_repayment(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50 }]
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) 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_security_pledge.name,
posting_date=get_first_day(nowdate()))
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEquals(loan.loan_amount, 1000000)
@ -166,16 +164,15 @@ class TestLoan(unittest.TestCase):
penalty_amount - amounts[0], 2)) penalty_amount - amounts[0], 2))
def test_loan_closure_repayment(self): def test_loan_closure_repayment(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50 }]
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, create_pledge(loan_application)
posting_date=get_first_day(nowdate()))
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEquals(loan.loan_amount, 1000000)
@ -214,23 +211,21 @@ class TestLoan(unittest.TestCase):
self.assertEquals(loan.status, "Loan Closure Requested") self.assertEquals(loan.status, "Loan Closure Requested")
def test_loan_repayment_for_term_loan(self): def test_loan_repayment_for_term_loan(self):
pledges = [] pledges = [{
pledges.append({
"loan_security": "Test Security 2", "loan_security": "Test Security 2",
"qty": 4000.00, "qty": 4000.00
"haircut": 50 },
}) {
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 2000.00, "qty": 2000.00
"haircut": 50 }]
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges,
"Repay Over Number of Periods", 12)
create_pledge(loan_application)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application,
loan_security_pledge.name, posting_date=add_months(nowdate(), -1)) posting_date=add_months(nowdate(), -1))
loan.submit() loan.submit()
@ -250,16 +245,18 @@ class TestLoan(unittest.TestCase):
self.assertEquals(amounts[1], 78303.00) self.assertEquals(amounts[1], 78303.00)
def test_security_shortfall(self): def test_security_shortfall(self):
pledges = [] pledges = [{
pledges.append({
"loan_security": "Test Security 2", "loan_security": "Test Security 2",
"qty": 8000.00, "qty": 8000.00,
"haircut": 50, "haircut": 50,
}) }]
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2,
'Stock Loan', pledges, "Repay Over Number of Periods", 12)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) create_pledge(loan_application)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
loan.submit() loan.submit()
make_loan_disbursement_entry(loan.name, loan.loan_amount) make_loan_disbursement_entry(loan.name, loan.loan_amount)
@ -279,16 +276,15 @@ class TestLoan(unittest.TestCase):
where loan_security='Test Security 2'""") where loan_security='Test Security 2'""")
def test_loan_security_unpledge(self): def test_loan_security_unpledge(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50 }]
})
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, create_pledge(loan_application)
posting_date=get_first_day(nowdate()))
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEquals(loan.loan_amount, 1000000)
@ -446,12 +442,13 @@ def create_loan_security():
"haircut": 50.00, "haircut": 50.00,
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
def create_loan_security_pledge(applicant, pledges): def create_loan_security_pledge(applicant, pledges, loan_application):
lsp = frappe.new_doc("Loan Security Pledge") lsp = frappe.new_doc("Loan Security Pledge")
lsp.applicant_type = 'Customer' lsp.applicant_type = 'Customer'
lsp.applicant = applicant lsp.applicant = applicant
lsp.company = "_Test Company" lsp.company = "_Test Company"
lsp.loan_application = loan_application
for pledge in pledges: for pledge in pledges:
lsp.append('securities', { lsp.append('securities', {
@ -510,6 +507,31 @@ def create_repayment_entry(loan, applicant, posting_date, payment_type, paid_amo
return lr return lr
def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None,
repayment_periods=None, posting_date=None):
loan_application = frappe.new_doc('Loan Application')
loan_application.applicant_type = 'Customer'
loan_application.company = company
loan_application.applicant = applicant
loan_application.loan_type = loan_type
loan_application.posting_date = posting_date or nowdate()
loan_application.is_secured_loan = 1
if repayment_method:
loan_application.repayment_method = repayment_method
loan_application.repayment_periods = repayment_periods
for pledge in proposed_pledges:
loan_application.append('proposed_pledges', pledge)
loan_application.save()
loan_application.submit()
loan_application.status = 'Approved'
loan_application.save()
return loan_application.name
def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods, def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods,
repayment_start_date=None, posting_date=None): repayment_start_date=None, posting_date=None):
@ -531,14 +553,13 @@ def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_p
loan.save() loan.save()
return loan return loan
def create_loan_with_security(applicant, loan_type, repayment_method, repayment_periods, loan_security_pledge, def create_loan_with_security(applicant, loan_type, repayment_method, repayment_periods, loan_application, posting_date=None, repayment_start_date=None):
posting_date=None, repayment_start_date=None):
loan = frappe.get_doc({ loan = frappe.get_doc({
"doctype": "Loan", "doctype": "Loan",
"company": "_Test Company", "company": "_Test Company",
"applicant_type": "Customer", "applicant_type": "Customer",
"posting_date": posting_date or nowdate(), "posting_date": posting_date or nowdate(),
"loan_application": loan_application,
"applicant": applicant, "applicant": applicant,
"loan_type": loan_type, "loan_type": loan_type,
"is_term_loan": 1, "is_term_loan": 1,
@ -547,7 +568,6 @@ def create_loan_with_security(applicant, loan_type, repayment_method, repayment_
"repayment_periods": repayment_periods, "repayment_periods": repayment_periods,
"repayment_start_date": repayment_start_date or nowdate(), "repayment_start_date": repayment_start_date or nowdate(),
"mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'), "mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
"loan_security_pledge": loan_security_pledge,
"payment_account": 'Payment Account - _TC', "payment_account": 'Payment Account - _TC',
"loan_account": 'Loan Account - _TC', "loan_account": 'Loan Account - _TC',
"interest_income_account": 'Interest Income Account - _TC', "interest_income_account": 'Interest Income Account - _TC',
@ -558,19 +578,19 @@ def create_loan_with_security(applicant, loan_type, repayment_method, repayment_
return loan return loan
def create_demand_loan(applicant, loan_type, loan_security_pledge, posting_date=None): def create_demand_loan(applicant, loan_type, loan_application, posting_date=None):
loan = frappe.get_doc({ loan = frappe.get_doc({
"doctype": "Loan", "doctype": "Loan",
"company": "_Test Company", "company": "_Test Company",
"applicant_type": "Customer", "applicant_type": "Customer",
"posting_date": posting_date or nowdate(), "posting_date": posting_date or nowdate(),
'loan_application': loan_application,
"applicant": applicant, "applicant": applicant,
"loan_type": loan_type, "loan_type": loan_type,
"is_term_loan": 0, "is_term_loan": 0,
"is_secured_loan": 1, "is_secured_loan": 1,
"mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'), "mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
"loan_security_pledge": loan_security_pledge,
"payment_account": 'Payment Account - _TC', "payment_account": 'Payment Account - _TC',
"loan_account": 'Loan Account - _TC', "loan_account": 'Loan Account - _TC',
"interest_income_account": 'Interest Income Account - _TC', "interest_income_account": 'Interest Income Account - _TC',

View File

@ -103,10 +103,13 @@ class LoanApplication(Document):
if self.is_secured_loan and not self.proposed_pledges: if self.is_secured_loan and not self.proposed_pledges:
frappe.throw(_("Proposed Pledges are mandatory for secured Loans")) frappe.throw(_("Proposed Pledges are mandatory for secured Loans"))
if not self.loan_amount and self.is_secured_loan and self.proposed_pledges: if self.is_secured_loan and self.proposed_pledges:
self.loan_amount = 0 self.maximum_loan_amount = 0
for security in self.proposed_pledges: for security in self.proposed_pledges:
self.loan_amount += security.post_haircut_amount self.maximum_loan_amount += security.post_haircut_amount
if not self.loan_amount and self.is_secured_loan and self.proposed_pledges:
self.loan_amount = self.maximum_loan_amount
@frappe.whitelist() @frappe.whitelist()
def create_loan(source_name, target_doc=None, submit=0): def create_loan(source_name, target_doc=None, submit=0):
@ -116,7 +119,6 @@ def create_loan(source_name, target_doc=None, submit=0):
filters = {'name': source_doc.loan_type} filters = {'name': source_doc.loan_type}
)[0] )[0]
loan_security_pledge = frappe.db.get_value("Loan Security Pledge", {"loan_application": source_name}, 'name')
target_doc.mode_of_payment = account_details.mode_of_payment target_doc.mode_of_payment = account_details.mode_of_payment
target_doc.payment_account = account_details.payment_account target_doc.payment_account = account_details.payment_account
@ -124,9 +126,6 @@ def create_loan(source_name, target_doc=None, submit=0):
target_doc.interest_income_account = account_details.interest_income_account target_doc.interest_income_account = account_details.interest_income_account
target_doc.penalty_income_account = account_details.penalty_income_account target_doc.penalty_income_account = account_details.penalty_income_account
if loan_security_pledge:
target_doc.is_secured_loan = 1
target_doc.loan_security_pledge = loan_security_pledge
doclist = get_mapped_doc("Loan Application", source_name, { doclist = get_mapped_doc("Loan Application", source_name, {
"Loan Application": { "Loan Application": {

View File

@ -5,11 +5,12 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date)
from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, create_loan_application,
make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price) make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
from erpnext.selling.doctype.customer.test_customer import get_customer_dict from erpnext.selling.doctype.customer.test_customer import get_customer_dict
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
class TestLoanDisbursement(unittest.TestCase): class TestLoanDisbursement(unittest.TestCase):
@ -31,18 +32,15 @@ class TestLoanDisbursement(unittest.TestCase):
self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name') self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
def test_loan_topup(self): def test_loan_topup(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50, }]
"loan_security_price": 500.00
})
loan_security_pledge = create_loan_security_pledge(self.applicant, pledges) loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
create_pledge(loan_application)
loan = create_demand_loan(self.applicant, "Demand Loan", loan_security_pledge.name, loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
posting_date=get_first_day(nowdate()))
loan.submit() loan.submit()

View File

@ -6,10 +6,11 @@ import frappe
import unittest import unittest
from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date)
from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price,
make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_application)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
from erpnext.selling.doctype.customer.test_customer import get_customer_dict from erpnext.selling.doctype.customer.test_customer import get_customer_dict
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
class TestLoanInterestAccrual(unittest.TestCase): class TestLoanInterestAccrual(unittest.TestCase):
def setUp(self): def setUp(self):
@ -29,17 +30,15 @@ class TestLoanInterestAccrual(unittest.TestCase):
self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name') self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
def test_loan_interest_accural(self): def test_loan_interest_accural(self):
pledges = [] pledge = [{
pledges.append({
"loan_security": "Test Security 1", "loan_security": "Test Security 1",
"qty": 4000.00, "qty": 4000.00
"haircut": 50, }]
"loan_security_price": 500.00
})
loan_security_pledge = create_loan_security_pledge(self.applicant, pledges) loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
create_pledge(loan_application)
loan = create_demand_loan(self.applicant, "Demand Loan", loan_security_pledge.name, loan = create_demand_loan(self.applicant, "Demand Loan", loan_application,
posting_date=get_first_day(nowdate())) posting_date=get_first_day(nowdate()))
loan.submit() loan.submit()

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "LS-.{applicant}.-.#####", "autoname": "LS-.{applicant}.-.#####",
"creation": "2019-08-29 18:48:51.371674", "creation": "2019-08-29 18:48:51.371674",
"doctype": "DocType", "doctype": "DocType",
@ -6,10 +7,10 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"loan_details_section", "loan_details_section",
"loan_application",
"loan",
"applicant_type", "applicant_type",
"applicant", "applicant",
"loan",
"loan_application",
"column_break_3", "column_break_3",
"company", "company",
"pledge_time", "pledge_time",
@ -55,15 +56,13 @@
"fieldname": "loan", "fieldname": "loan",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Loan", "label": "Loan",
"options": "Loan", "options": "Loan"
"read_only": 1
}, },
{ {
"fieldname": "loan_application", "fieldname": "loan_application",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Loan Application", "label": "Loan Application",
"options": "Loan Application", "options": "Loan Application"
"read_only": 1
}, },
{ {
"fieldname": "total_security_value", "fieldname": "total_security_value",
@ -133,7 +132,8 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-10-10 13:22:53.297519", "links": [],
"modified": "2020-07-02 23:38:24.002382",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan Security Pledge", "name": "Loan Security Pledge",

View File

@ -192,7 +192,7 @@ def get_number_cards():
]), ]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("Monthly Total Work Order"), "label": _("Monthly Total Work Orders"),
"show_percentage_stats": 1, "show_percentage_stats": 1,
"stats_time_interval": "Weekly" "stats_time_interval": "Weekly"
}, },
@ -207,7 +207,7 @@ def get_number_cards():
]), ]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("Monthly Completed Work Order"), "label": _("Monthly Completed Work Orders"),
"show_percentage_stats": 1, "show_percentage_stats": 1,
"stats_time_interval": "Weekly" "stats_time_interval": "Weekly"
}, },
@ -221,7 +221,7 @@ def get_number_cards():
]), ]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("Ongoing Job Card"), "label": _("Ongoing Job Cards"),
"show_percentage_stats": 1, "show_percentage_stats": 1,
"stats_time_interval": "Weekly" "stats_time_interval": "Weekly"
}, },
@ -235,7 +235,7 @@ def get_number_cards():
]), ]),
"function": "Count", "function": "Count",
"is_public": 1, "is_public": 1,
"label": _("Monthly Quality Inspection"), "label": _("Monthly Quality Inspections"),
"show_percentage_stats": 1, "show_percentage_stats": 1,
"stats_time_interval": "Weekly" "stats_time_interval": "Weekly"
}] }]

View File

@ -93,12 +93,6 @@
"stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}",
"type": "DocType" "type": "DocType"
}, },
{
"label": "Dashboard",
"link_to": "Manufacturing",
"restrict_to_domain": "Manufacturing",
"type": "Dashboard"
},
{ {
"label": "Forecasting", "label": "Forecasting",
"link_to": "Exponential Smoothing Forecasting", "link_to": "Exponential Smoothing Forecasting",
@ -119,6 +113,12 @@
"label": "Production Planning Report", "label": "Production Planning Report",
"link_to": "Production Planning Report", "link_to": "Production Planning Report",
"type": "Report" "type": "Report"
} },
{
"label": "Dashboard",
"link_to": "Manufacturing",
"restrict_to_domain": "Manufacturing",
"type": "Dashboard"
}
] ]
} }

View File

@ -910,6 +910,7 @@ def get_bom_diff(bom1, bom2):
return out return out
@frappe.whitelist()
def item_query(doctype, txt, searchfield, start, page_len, filters): def item_query(doctype, txt, searchfield, start, page_len, filters):
meta = frappe.get_meta("Item", cached=True) meta = frappe.get_meta("Item", cached=True)
searchfields = meta.get_search_fields() searchfields = meta.get_search_fields()
@ -989,4 +990,4 @@ def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None):
}, },
}, target_doc, postprocess) }, target_doc, postprocess)
return doc return doc

View File

@ -631,6 +631,7 @@ class WorkOrder(Document):
bom.set_bom_material_details() bom.set_bom_material_details()
return bom return bom
@frappe.whitelist()
def get_bom_operations(doctype, txt, searchfield, start, page_len, filters): def get_bom_operations(doctype, txt, searchfield, start, page_len, filters):
if txt: if txt:
filters['operation'] = ('like', '%%%s%%' % txt) filters['operation'] = ('like', '%%%s%%' % txt)

View File

@ -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-05-19 12:51:42.744570", "modified": "2020-06-29 20:25:36.899106",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Manufacturing", "name": "Manufacturing",
@ -52,6 +52,5 @@
], ],
"subtitle": "Products, Raw Materials, BOM, Work Order and more.", "subtitle": "Products, Raw Materials, BOM, Work Order and more.",
"success_message": "Manufacturing module is all setup!", "success_message": "Manufacturing module is all setup!",
"title": "Let's Setup Manufacturing Module", "title": "Let's Set Up the Manufacturing Module"
"user_can_dismiss": 1 }
}

View File

@ -19,7 +19,7 @@ def get_columns(filters):
"options": "Work Order", "options": "Work Order",
"width": 120 "width": 120
}] }]
if not filters.get('bom_no'): if not filters.get('bom_no'):
columns.extend([ columns.extend([
{ {

View File

@ -707,3 +707,4 @@ erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020
erpnext.patches.v12_0.add_taxjar_integration_field erpnext.patches.v12_0.add_taxjar_integration_field
erpnext.patches.v12_0.update_item_tax_template_company

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('accounts', 'doctype', 'item_tax_template')
item_tax_template_list = frappe.get_list('Item Tax Template')
for template in item_tax_template_list:
doc = frappe.get_doc('Item Tax Template', template.name)
for tax in doc.taxes:
doc.company = frappe.get_value('Account', tax.tax_type, 'company')
break
doc.save()

View File

@ -222,7 +222,7 @@ def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
return benefit_amount return benefit_amount
@frappe.whitelist()
def get_earning_components(doctype, txt, searchfield, start, page_len, filters): def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
if len(filters) < 2: if len(filters) < 2:
return {} return {}

View File

@ -30,6 +30,7 @@ frappe.ui.form.on('Payroll Entry', {
).toggleClass('btn-primary', !(frm.doc.employees || []).length); ).toggleClass('btn-primary', !(frm.doc.employees || []).length);
} }
if ((frm.doc.employees || []).length) { if ((frm.doc.employees || []).length) {
frm.page.clear_primary_action();
frm.page.set_primary_action(__('Create Salary Slips'), () => { frm.page.set_primary_action(__('Create Salary Slips'), () => {
frm.save('Submit').then(()=>{ frm.save('Submit').then(()=>{
frm.page.clear_primary_action(); frm.page.clear_primary_action();
@ -49,13 +50,14 @@ frappe.ui.form.on('Payroll Entry', {
return frappe.call({ return frappe.call({
doc: frm.doc, doc: frm.doc,
method: 'fill_employee_details', method: 'fill_employee_details',
callback: function(r) { }).then(r => {
if (r.docs[0].employees){ if (r.docs && r.docs[0].employees){
frm.save(); frm.employees = r.docs[0].employees;
frm.refresh(); frm.dirty();
if(r.docs[0].validate_attendance){ frm.save();
render_employee_attendance(frm, r.message); frm.refresh();
} if(r.docs[0].validate_attendance){
render_employee_attendance(frm, r.message);
} }
} }
}) })

View File

@ -539,6 +539,7 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
if not_submitted_ss: if not_submitted_ss:
frappe.msgprint(_("Could not submit some Salary Slips")) frappe.msgprint(_("Could not submit some Salary Slips"))
@frappe.whitelist()
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" return frappe.db.sql("""
select name from `tabPayroll Entry` select name from `tabPayroll Entry`

View File

@ -53,7 +53,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2020-06-22 20:12:32.684189", "modified": "2020-06-29 17:17:12.689089",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Payroll", "module": "Payroll",
"name": "Payroll Period", "name": "Payroll Period",
@ -96,6 +96,7 @@
"write": 1 "write": 1
} }
], ],
"quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1 "track_changes": 1

View File

@ -207,7 +207,7 @@ class SalarySlip(TransactionBase):
frappe.throw(_("There are more holidays than working days this month.")) frappe.throw(_("There are more holidays than working days this month."))
if not payroll_based_on: if not payroll_based_on:
frappe.throw(_("Please set Payroll based on in HR settings")) frappe.throw(_("Please set Payroll based on in Payroll settings"))
if payroll_based_on == "Attendance": if payroll_based_on == "Attendance":
actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays) actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays)
@ -244,15 +244,13 @@ class SalarySlip(TransactionBase):
for holiday in holidays: for holiday in holidays:
if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }): if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
self.payment_days += 1 self.payment_days += 1
else: else:
self.payment_days = 0 self.payment_days = 0
def get_unmarked_days(self): def get_unmarked_days(self):
marked_days = frappe.get_all("Attendance", filters = { marked_days = frappe.get_all("Attendance", filters = {
"attendance_date": ["between", ['2020-05-1',"2020-05-30"]], "attendance_date": ["between", [self.start_date, self.end_date]],
"employee": 'HR-EMP-00003', "employee": self.employee,
"docstatus": 1 "docstatus": 1
}, fields = ["COUNT(*) as marked_days"])[0].marked_days }, fields = ["COUNT(*) as marked_days"])[0].marked_days

View File

@ -105,7 +105,7 @@ class TestSalarySlip(unittest.TestCase):
#Gross pay calculation based on attendances #Gross pay calculation based on attendances
gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay))
self.assertEqual(ss.gross_pay, gross_pay) self.assertEqual(flt(ss.gross_pay, 2), flt(gross_pay, 2))
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")

View File

@ -13,7 +13,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2020-06-04 16:35:30.650792", "modified": "2020-06-29 17:00:25.113341",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Payroll", "module": "Payroll",
"name": "Payroll", "name": "Payroll",

View File

@ -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",

View File

@ -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
} }

View File

@ -152,6 +152,9 @@ def set_company_account(payment_accounts, payroll_entries):
company_accounts_map[acc.account] = acc company_accounts_map[acc.account] = acc
for entry in payroll_entries: for entry in payroll_entries:
entry["company_account"] = company_accounts_map[entry.payment_account]['bank_account_no'] company_account = ''
if entry.payment_account in company_accounts_map:
company_account = company_accounts_map[entry.payment_account]['bank_account_no']
entry["company_account"] = company_account
return payroll_entries return payroll_entries

View File

@ -100,6 +100,8 @@ def get_data(filters, mode_of_payments):
total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) total_row = get_total_based_on_mode_of_payment(data, mode_of_payments)
total_deductions = gross_pay - total_row.get("total") total_deductions = gross_pay - total_row.get("total")
report_summary = []
if data: if data:
data.append(total_row) data.append(total_row)
data.append({}) data.append({})

View File

@ -68,7 +68,7 @@
"type": "Report" "type": "Report"
}, },
{ {
"label": "Project Dashboard", "label": "Dashboard",
"link_to": "Project", "link_to": "Project",
"type": "Dashboard" "type": "Dashboard"
} }

View File

@ -238,6 +238,7 @@ def get_list_context(context=None):
"row_template": "templates/includes/projects/project_row.html" "row_template": "templates/includes/projects/project_row.html"
} }
@frappe.whitelist()
def get_users_for_project(doctype, txt, searchfield, start, page_len, filters): def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
conditions = [] conditions = []
return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name) return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name)

View File

@ -183,7 +183,8 @@
{ {
"fieldname": "progress", "fieldname": "progress",
"fieldtype": "Percent", "fieldtype": "Percent",
"label": "% Progress" "label": "% Progress",
"no_copy": 1
}, },
{ {
"default": "0", "default": "0",
@ -356,6 +357,7 @@
"fieldname": "completed_by", "fieldname": "completed_by",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Completed By", "label": "Completed By",
"no_copy": 1,
"options": "User" "options": "User"
} }
], ],
@ -364,7 +366,7 @@
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"max_attachments": 5, "max_attachments": 5,
"modified": "2020-03-18 18:08:44.153211", "modified": "2020-07-03 12:36:04.960457",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Task", "name": "Task",

View File

@ -188,6 +188,7 @@ def check_if_child_exists(name):
return child_tasks return child_tasks
@frappe.whitelist()
def get_project(doctype, txt, searchfield, start, page_len, filters): def get_project(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
return frappe.db.sql(""" select name from `tabProject` return frappe.db.sql(""" select name from `tabProject`

View File

@ -1835,7 +1835,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if (doc.tax_category) if (doc.tax_category)
filters['tax_category'] = doc.tax_category; filters['tax_category'] = doc.tax_category;
if (doc.company)
filters['company'] = doc.company;
return { return {
query: "erpnext.controllers.queries.get_tax_template", query: "erpnext.controllers.queries.get_tax_template",
filters: filters filters: filters

View File

@ -4,7 +4,7 @@
frappe.provide("erpnext.utils"); frappe.provide("erpnext.utils");
erpnext.utils.get_party_details = function(frm, method, args, callback) { erpnext.utils.get_party_details = function(frm, method, args, callback) {
if(!method) { if (!method) {
method = "erpnext.accounts.party.get_party_details"; method = "erpnext.accounts.party.get_party_details";
} }
@ -22,12 +22,12 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
} }
} }
if(!args) { if (!args) {
if((frm.doctype != "Purchase Order" && frm.doc.customer) if ((frm.doctype != "Purchase Order" && frm.doc.customer)
|| (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) { || (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) {
let party_type = "Customer"; let party_type = "Customer";
if(frm.doc.quotation_to && frm.doc.quotation_to === "Lead") { if (frm.doc.quotation_to && frm.doc.quotation_to === "Lead") {
party_type = "Lead"; party_type = "Lead";
} }
@ -36,7 +36,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
party_type: party_type, party_type: party_type,
price_list: frm.doc.selling_price_list price_list: frm.doc.selling_price_list
}; };
} else if(frm.doc.supplier) { } else if (frm.doc.supplier) {
args = { args = {
party: frm.doc.supplier, party: frm.doc.supplier,
party_type: "Supplier", party_type: "Supplier",
@ -78,13 +78,17 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
} }
} }
if(!args || !args.party) return; if (!args || !args.party) return;
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",
args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return; args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return;
} }
if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, args.party_type=="Customer" ? "customer": "supplier")) {
return;
}
args.currency = frm.doc.currency; args.currency = frm.doc.currency;
args.company = frm.doc.company; args.company = frm.doc.company;
args.doctype = frm.doc.doctype; args.doctype = frm.doc.doctype;
@ -92,14 +96,14 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
method: method, method: method,
args: args, args: args,
callback: function(r) { callback: function(r) {
if(r.message) { if (r.message) {
frm.supplier_tds = r.message.supplier_tds; frm.supplier_tds = r.message.supplier_tds;
frm.updating_party_details = true; frm.updating_party_details = true;
frappe.run_serially([ frappe.run_serially([
() => frm.set_value(r.message), () => frm.set_value(r.message),
() => { () => {
frm.updating_party_details = false; frm.updating_party_details = false;
if(callback) callback(); if (callback) callback();
frm.refresh(); frm.refresh();
erpnext.utils.add_item(frm); erpnext.utils.add_item(frm);
} }
@ -110,9 +114,9 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
} }
erpnext.utils.add_item = function(frm) { erpnext.utils.add_item = function(frm) {
if(frm.is_new()) { if (frm.is_new()) {
var prev_route = frappe.get_prev_route(); var prev_route = frappe.get_prev_route();
if(prev_route[1]==='Item' && !(frm.doc.items && frm.doc.items.length)) { if (prev_route[1]==='Item' && !(frm.doc.items && frm.doc.items.length)) {
// add row // add row
var item = frm.add_child('items'); var item = frm.add_child('items');
frm.refresh_field('items'); frm.refresh_field('items');
@ -124,23 +128,23 @@ erpnext.utils.add_item = function(frm) {
} }
erpnext.utils.get_address_display = function(frm, address_field, display_field, is_your_company_address) { erpnext.utils.get_address_display = function(frm, address_field, display_field, is_your_company_address) {
if(frm.updating_party_details) return; if (frm.updating_party_details) return;
if(!address_field) { if (!address_field) {
if(frm.doctype != "Purchase Order" && frm.doc.customer) { if (frm.doctype != "Purchase Order" && frm.doc.customer) {
address_field = "customer_address"; address_field = "customer_address";
} else if(frm.doc.supplier) { } else if (frm.doc.supplier) {
address_field = "supplier_address"; address_field = "supplier_address";
} else return; } else return;
} }
if(!display_field) display_field = "address_display"; if (!display_field) display_field = "address_display";
if(frm.doc[address_field]) { if (frm.doc[address_field]) {
frappe.call({ frappe.call({
method: "frappe.contacts.doctype.address.address.get_address_display", method: "frappe.contacts.doctype.address.address.get_address_display",
args: {"address_dict": frm.doc[address_field] }, args: {"address_dict": frm.doc[address_field] },
callback: function(r) { callback: function(r) {
if(r.message) { if (r.message) {
frm.set_value(display_field, r.message) frm.set_value(display_field, r.message)
} }
} }
@ -151,15 +155,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field,
}; };
erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billing_address_field, shipping_address_field) { erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billing_address_field, shipping_address_field) {
if(frm.updating_party_details) return; if (frm.updating_party_details) return;
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
if(!erpnext.utils.validate_mandatory(frm, "Lead/Customer/Supplier", if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier",
frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) {
return; return;
} }
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",
frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) {
return; return;
} }
@ -175,8 +179,8 @@ erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billi
"shipping_address": frm.doc[shipping_address_field] "shipping_address": frm.doc[shipping_address_field]
}, },
callback: function(r) { callback: function(r) {
if(!r.exc){ if (!r.exc){
if(frm.doc.tax_category != r.message) { if (frm.doc.tax_category != r.message) {
frm.set_value("tax_category", r.message); frm.set_value("tax_category", r.message);
} else { } else {
erpnext.utils.set_taxes(frm, triggered_from_field); erpnext.utils.set_taxes(frm, triggered_from_field);
@ -187,13 +191,17 @@ erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billi
}; };
erpnext.utils.set_taxes = function(frm, triggered_from_field) { erpnext.utils.set_taxes = function(frm, triggered_from_field) {
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
if(!erpnext.utils.validate_mandatory(frm, "Lead/Customer/Supplier", if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, triggered_from_field)) {
return;
}
if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier",
frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) {
return; return;
} }
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",
frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) {
return; return;
} }
@ -230,7 +238,7 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) {
"shipping_address": frm.doc.shipping_address_name "shipping_address": frm.doc.shipping_address_name
}, },
callback: function(r) { callback: function(r) {
if(r.message){ if (r.message){
frm.set_value("taxes_and_charges", r.message) frm.set_value("taxes_and_charges", r.message)
} }
} }
@ -238,14 +246,14 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) {
}; };
erpnext.utils.get_contact_details = function(frm) { erpnext.utils.get_contact_details = function(frm) {
if(frm.updating_party_details) return; if (frm.updating_party_details) return;
if(frm.doc["contact_person"]) { if (frm.doc["contact_person"]) {
frappe.call({ frappe.call({
method: "frappe.contacts.doctype.contact.contact.get_contact_details", method: "frappe.contacts.doctype.contact.contact.get_contact_details",
args: {contact: frm.doc.contact_person }, args: {contact: frm.doc.contact_person },
callback: function(r) { callback: function(r) {
if(r.message) if (r.message)
frm.set_value(r.message); frm.set_value(r.message);
} }
}) })
@ -253,10 +261,10 @@ erpnext.utils.get_contact_details = function(frm) {
} }
erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
if(!value) { if (!value) {
frm.doc[trigger_on] = ""; frm.doc[trigger_on] = "";
refresh_field(trigger_on); refresh_field(trigger_on);
frappe.msgprint(__("Please enter {0} first", [label])); frappe.throw({message:__("Please enter {0} first", [label]), title:__("Mandatory")});
return false; return false;
} }
return true; return true;
@ -271,12 +279,12 @@ erpnext.utils.get_shipping_address = function(frm, callback){
address: frm.doc.shipping_address address: frm.doc.shipping_address
}, },
callback: function(r){ callback: function(r){
if(r.message){ if (r.message){
frm.set_value("shipping_address", r.message[0]) //Address title or name frm.set_value("shipping_address", r.message[0]) //Address title or name
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
} }
if(callback){ if (callback){
return callback(); return callback();
} }
} }

View File

@ -5,21 +5,28 @@ frappe.ui.form.on('Quality Feedback', {
refresh: function(frm) { refresh: function(frm) {
frm.set_value("date", frappe.datetime.get_today()); frm.set_value("date", frappe.datetime.get_today());
}, },
template: function(frm){
frappe.call({ template: function(frm) {
"method": "frappe.client.get", if (frm.doc.template) {
args: { frappe.call({
doctype: "Quality Feedback Template", "method": "frappe.client.get",
name: frm.doc.template args: {
}, doctype: "Quality Feedback Template",
callback: function(data){ name: frm.doc.template
frm.fields_dict.parameters.grid.remove_all(); },
for (var i in data.message.parameters){ callback: function(data) {
frm.add_child("parameters"); if (data && data.message) {
frm.fields_dict.parameters.get_value()[i].parameter = data.message.parameters[i].parameter; frm.fields_dict.parameters.grid.remove_all();
// fetch parameters from template and autofill
for (let template_parameter of data.message.parameters) {
let row = frm.add_child("parameters");
row.parameter = template_parameter.parameter;
}
frm.refresh();
}
} }
frm.refresh(); });
} }
});
} }
}); });

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "format:FDBK-{#####}", "autoname": "format:FDBK-{#####}",
"creation": "2019-05-26 21:23:05.308379", "creation": "2019-05-26 21:23:05.308379",
"doctype": "DocType", "doctype": "DocType",
@ -53,12 +54,13 @@
{ {
"fieldname": "document_name", "fieldname": "document_name",
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"label": "Name", "label": "Feedback By",
"options": "document_type", "options": "document_type",
"reqd": 1 "reqd": 1
} }
], ],
"modified": "2019-05-28 15:16:01.161662", "links": [],
"modified": "2020-07-03 15:50:58.589302",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Feedback", "name": "Quality Feedback",

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "format:TMPL-{template}", "autoname": "format:TMPL-{template}",
"creation": "2019-05-26 21:17:24.283061", "creation": "2019-05-26 21:17:24.283061",
"doctype": "DocType", "doctype": "DocType",
@ -30,10 +31,12 @@
"fieldname": "parameters", "fieldname": "parameters",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Parameters", "label": "Parameters",
"options": "Quality Feedback Template Parameter" "options": "Quality Feedback Template Parameter",
"reqd": 1
} }
], ],
"modified": "2019-05-26 21:48:47.770610", "links": [],
"modified": "2020-07-03 16:06:03.749415",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Feedback Template", "name": "Quality Feedback Template",

View File

@ -101,7 +101,7 @@ def prepare_data(entry,component_type_dict):
"employee": d.employee, "employee": d.employee,
"employee_name": d.employee_name, "employee_name": d.employee_name,
"pf_account": employee_account_dict.get(d.employee), "pf_account": employee_account_dict.get(d.employee),
"component_type": d.amount component_type: d.amount
}) })
return data_list return data_list

View File

@ -11,14 +11,17 @@ def update_itemised_tax_data(doc):
for row in doc.items: for row in doc.items:
tax_rate = 0.0 tax_rate = 0.0
item_tax_rate = frappe.parse_json(row.item_tax_rate) item_tax_rate = 0.0
if row.item_tax_rate:
item_tax_rate = frappe.parse_json(row.item_tax_rate)
# First check if tax rate is present # First check if tax rate is present
# If not then look up in item_wise_tax_detail # If not then look up in item_wise_tax_detail
if item_tax_rate: if item_tax_rate:
for account, rate in iteritems(item_tax_rate): for account, rate in iteritems(item_tax_rate):
tax_rate += rate tax_rate += rate
elif itemised_tax.get(row.item_code): elif row.item_code and itemised_tax.get(row.item_code):
tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
row.tax_rate = flt(tax_rate, row.precision("tax_rate")) row.tax_rate = flt(tax_rate, row.precision("tax_rate"))

View File

@ -65,6 +65,7 @@ def make_invoice(table, customer, mode_of_payment):
return invoice.name return invoice.name
@frappe.whitelist()
def item_query_restaurant(doctype='Item', txt='', searchfield='name', start=0, page_len=20, filters=None, as_dict=False): def item_query_restaurant(doctype='Item', txt='', searchfield='name', start=0, page_len=20, filters=None, as_dict=False):
'''Return items that are selected in active menu of the restaurant''' '''Return items that are selected in active menu of the restaurant'''
restaurant, menu = get_restaurant_and_menu_name(filters['table']) restaurant, menu = get_restaurant_and_menu_name(filters['table'])
@ -84,4 +85,4 @@ def get_restaurant_and_menu_name(table):
if not menu: if not menu:
frappe.throw(_('Please set an active menu for Restaurant {0}').format(restaurant)) frappe.throw(_('Please set an active menu for Restaurant {0}').format(restaurant))
return restaurant, menu return restaurant, menu

View File

@ -23,7 +23,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Other Reports", "label": "Other Reports",
"links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]"
} }
], ],
"category": "Modules", "category": "Modules",
@ -44,7 +44,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Selling", "label": "Selling",
"modified": "2020-06-19 13:23:24.861706", "modified": "2020-06-29 19:26:35.139097",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Selling", "name": "Selling",

View File

@ -342,11 +342,10 @@ def get_loyalty_programs(doc):
@frappe.whitelist() @frappe.whitelist()
def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
from erpnext.controllers.queries import get_fields from erpnext.controllers.queries import get_fields
fields = ["name", "customer_name", "customer_group", "territory"]
if frappe.db.get_default("cust_master_name") == "Customer Name": if frappe.db.get_default("cust_master_name") == "Customer Name":
fields = ["name", "customer_group", "territory"] fields = ["name", "customer_group", "territory"]
else:
fields = ["name", "customer_name", "customer_group", "territory"]
fields = get_fields("Customer", fields) fields = get_fields("Customer", fields)
@ -542,6 +541,7 @@ def make_address(args, is_primary_address=1):
return address return address
@frappe.whitelist()
def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters): def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters):
customer = filters.get('customer') customer = filters.get('customer')
return frappe.db.sql(""" return frappe.db.sql("""
@ -552,4 +552,4 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil
""", { """, {
'customer': customer, 'customer': customer,
'txt': '%%%s%%' % txt 'txt': '%%%s%%' % txt
}) })

View File

@ -22,12 +22,13 @@ class ProductBundle(Document):
"""Validates, main Item is not a stock item""" """Validates, main Item is not a stock item"""
if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"): if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"):
frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code)) frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code))
def validate_child_items(self): def validate_child_items(self):
for item in self.items: for item in self.items:
if frappe.db.exists("Product Bundle", item.item_code): if frappe.db.exists("Product Bundle", item.item_code):
frappe.throw(_("Child Item should not be a Product Bundle. Please remove item `{0}` and save").format(item.item_code)) frappe.throw(_("Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save").format(item.idx, frappe.bold(item.item_code)))
@frappe.whitelist()
def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond

File diff suppressed because it is too large Load Diff

View File

@ -167,6 +167,7 @@ def get_item_group_condition(pos_profile):
return cond % tuple(item_groups) return cond % tuple(item_groups)
@frappe.whitelist()
def item_group_query(doctype, txt, searchfield, start, page_len, filters): def item_group_query(doctype, txt, searchfield, start, page_len, filters):
item_groups = [] item_groups = []
cond = "1=1" cond = "1=1"
@ -187,4 +188,4 @@ def item_group_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist() @frappe.whitelist()
def get_pos_fields(): def get_pos_fields():
return frappe.get_all("POS Field", fields=["label", "fieldname", return frappe.get_all("POS Field", fields=["label", "fieldname",
"fieldtype", "default_value", "reqd", "read_only", "options"]) "fieldtype", "default_value", "reqd", "read_only", "options"])

View File

@ -6,9 +6,12 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet, get_root_of
class CustomerGroup(NestedSet): class CustomerGroup(NestedSet):
nsm_parent_field = 'parent_customer_group' nsm_parent_field = 'parent_customer_group'
def validate(self):
if not self.parent_customer_group:
self.parent_customer_group = get_root_of("Customer Group")
def on_update(self): def on_update(self):
self.validate_name_with_customer() self.validate_name_with_customer()

View File

@ -5,13 +5,16 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet, get_root_of
from erpnext import get_default_currency from erpnext import get_default_currency
class SalesPerson(NestedSet): class SalesPerson(NestedSet):
nsm_parent_field = 'parent_sales_person' nsm_parent_field = 'parent_sales_person'
def validate(self): def validate(self):
if not self.parent_sales_person:
self.parent_sales_person = get_root_of("Sales Person")
for d in self.get('targets') or []: for d in self.get('targets') or []:
if not flt(d.target_qty) and not flt(d.target_amount): if not flt(d.target_qty) and not flt(d.target_amount):
frappe.throw(_("Either target qty or target amount is mandatory.")) frappe.throw(_("Either target qty or target amount is mandatory."))

View File

@ -6,12 +6,14 @@ import frappe
from frappe.utils import flt from frappe.utils import flt
from frappe import _ from frappe import _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet, get_root_of
class Territory(NestedSet): class Territory(NestedSet):
nsm_parent_field = 'parent_territory' nsm_parent_field = 'parent_territory'
def validate(self): def validate(self):
if not self.parent_territory:
self.parent_territory = get_root_of("Territory")
for d in self.get('targets') or []: for d in self.get('targets') or []:
if not flt(d.target_qty) and not flt(d.target_amount): if not flt(d.target_qty) and not flt(d.target_amount):

View File

@ -987,6 +987,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"collapsible": 1,
"depends_on": "eval:(!doc.is_item_from_hub)", "depends_on": "eval:(!doc.is_item_from_hub)",
"fieldname": "hub_publishing_sb", "fieldname": "hub_publishing_sb",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -1060,7 +1061,7 @@
"image_field": "image", "image_field": "image",
"links": [], "links": [],
"max_attachments": 1, "max_attachments": 1,
"modified": "2020-04-08 15:56:06.195722", "modified": "2020-06-30 12:01:07.534447",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item", "name": "Item",
@ -1122,4 +1123,4 @@
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "item_name", "title_field": "item_name",
"track_changes": 1 "track_changes": 1
} }

View File

@ -42,6 +42,7 @@ class ItemAlternative(Document):
'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}): 'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}):
frappe.throw(_("Already record exists for the item {0}").format(self.item_code)) frappe.throw(_("Already record exists for the item {0}").format(self.item_code))
@frappe.whitelist()
def get_alternative_items(doctype, txt, searchfield, start, page_len, filters): def get_alternative_items(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative` return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative`
where item_code = %(item_code)s and alternative_item_code like %(txt)s) where item_code = %(item_code)s and alternative_item_code like %(txt)s)
@ -52,4 +53,4 @@ def get_alternative_items(doctype, txt, searchfield, start, page_len, filters):
""".format(start, page_len), { """.format(start, page_len), {
"item_code": filters.get('item_code'), "item_code": filters.get('item_code'),
"txt": '%' + txt + '%' "txt": '%' + txt + '%'
}) })

View File

@ -172,7 +172,7 @@
"default": "Today", "default": "Today",
"fieldname": "valid_from", "fieldname": "valid_from",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Valid From " "label": "Valid From"
}, },
{ {
"default": "0", "default": "0",
@ -187,7 +187,7 @@
{ {
"fieldname": "valid_upto", "fieldname": "valid_upto",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Valid Upto " "label": "Valid Upto"
}, },
{ {
"fieldname": "section_break_24", "fieldname": "section_break_24",
@ -208,7 +208,7 @@
"icon": "fa fa-flag", "icon": "fa fa-flag",
"idx": 1, "idx": 1,
"links": [], "links": [],
"modified": "2020-02-28 14:21:25.580331", "modified": "2020-07-06 22:31:32.943475",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item Price", "name": "Item Price",

View File

@ -1,5 +1,4 @@
{ {
"actions": [],
"creation": "2013-02-22 01:28:01", "creation": "2013-02-22 01:28:01",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@ -38,8 +37,7 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "modified": "2020-06-25 01:40:28.859752",
"modified": "2019-12-28 21:54:40.807849",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item Tax", "name": "Item Tax",

View File

@ -402,6 +402,7 @@ def get_material_requests_based_on_supplier(doctype, txt, searchfield, start, pa
return material_requests return material_requests
@frappe.whitelist()
def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters): def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters):
doc = frappe.get_doc("Material Request", filters.get("doc")) doc = frappe.get_doc("Material Request", filters.get("doc"))
item_list = [] item_list = []
@ -567,4 +568,4 @@ def create_pick_list(source_name, target_doc=None):
doc.set_item_locations() doc.set_item_locations()
return doc return doc

View File

@ -175,6 +175,7 @@ class PackingSlip(Document):
self.update_item_details() self.update_item_details()
@frappe.whitelist()
def item_details(doctype, txt, searchfield, start, page_len, filters): def item_details(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
return frappe.db.sql("""select name, item_name, description from `tabItem` return frappe.db.sql("""select name, item_name, description from `tabItem`

View File

@ -58,6 +58,7 @@ class QualityInspection(Document):
.format(parent_doc=self.reference_type, child_doc=doctype), .format(parent_doc=self.reference_type, child_doc=doctype),
(quality_inspection, self.modified, self.reference_name, self.item_code)) (quality_inspection, self.modified, self.reference_name, self.item_code))
@frappe.whitelist()
def item_query(doctype, txt, searchfield, start, page_len, filters): def item_query(doctype, txt, searchfield, start, page_len, filters):
if filters.get("from"): if filters.get("from"):
from frappe.desk.reportview import get_match_cond from frappe.desk.reportview import get_match_cond
@ -86,6 +87,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
page_len = page_len, qi_condition = qi_condition), page_len = page_len, qi_condition = qi_condition),
{'parent': filters.get('parent'), 'txt': "%%%s%%" % txt}) {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})
@frappe.whitelist()
def quality_inspection_query(doctype, txt, searchfield, start, page_len, filters): def quality_inspection_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.get_all('Quality Inspection', return frappe.get_all('Quality Inspection',
limit_start=start, limit_start=start,
@ -118,4 +120,4 @@ def make_quality_inspection(source_name, target_doc=None):
} }
}, target_doc, postprocess) }, target_doc, postprocess)
return doc return doc

View File

@ -20,7 +20,7 @@ frappe.tour['Stock Settings'] = [
{ {
fieldname: "item_naming_by", fieldname: "item_naming_by",
title: __("Item Naming By"), title: __("Item Naming By"),
description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."), description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
}, },
{ {
fieldname: "default_warehouse", fieldname: "default_warehouse",

View File

@ -413,7 +413,7 @@ def get_item_tax_info(company, tax_category, item_codes):
continue continue
out[item_code] = {} out[item_code] = {}
item = frappe.get_cached_doc("Item", item_code) item = frappe.get_cached_doc("Item", item_code)
get_item_tax_template({"tax_category": tax_category}, item, out[item_code]) get_item_tax_template({"company": company, "tax_category": tax_category}, item, out[item_code])
out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True) out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True)
return out return out
@ -442,7 +442,8 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False):
taxes_with_no_validity = [] taxes_with_no_validity = []
for tax in taxes: for tax in taxes:
if tax.valid_from: tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company')
if tax.valid_from and tax_company == args['company']:
# In purchase Invoice first preference will be given to supplier invoice date # In purchase Invoice first preference will be given to supplier invoice date
# if supplier date is not present then posting date # if supplier date is not present then posting date
validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
@ -450,7 +451,8 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False):
if getdate(tax.valid_from) <= getdate(validation_date): if getdate(tax.valid_from) <= getdate(validation_date):
taxes_with_validity.append(tax) taxes_with_validity.append(tax)
else: else:
taxes_with_no_validity.append(tax) if tax_company == args['company']:
taxes_with_no_validity.append(tax)
if taxes_with_validity: if taxes_with_validity:
taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True) taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True)
@ -636,6 +638,10 @@ def get_item_price(args, item_code, ignore_party=False):
if args.get('transaction_date'): if args.get('transaction_date'):
conditions += """ and %(transaction_date)s between conditions += """ and %(transaction_date)s between
ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
if args.get('posting_date'):
conditions += """ and %(posting_date)s between
ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
return frappe.db.sql(""" select name, price_list_rate, uom return frappe.db.sql(""" select name, price_list_rate, uom
from `tabItem Price` {conditions} from `tabItem Price` {conditions}
@ -657,6 +663,7 @@ def get_price_list_rate_for(args, item_code):
"supplier": args.get('supplier'), "supplier": args.get('supplier'),
"uom": args.get('uom'), "uom": args.get('uom'),
"transaction_date": args.get('transaction_date'), "transaction_date": args.get('transaction_date'),
"posting_date": args.get('posting_date'),
} }
item_price_data = 0 item_price_data = 0

View File

@ -19,7 +19,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2020-05-19 19:03:23.602423", "modified": "2020-06-29 16:41:09.440378",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock", "name": "Stock",
@ -31,15 +31,15 @@
{ {
"step": "Create a Product" "step": "Create a Product"
}, },
{
"step": "Create a Supplier"
},
{ {
"step": "Introduction to Stock Entry" "step": "Introduction to Stock Entry"
}, },
{ {
"step": "Create a Stock Entry" "step": "Create a Stock Entry"
}, },
{
"step": "Create a Supplier"
},
{ {
"step": "Create a Purchase Receipt" "step": "Create a Purchase Receipt"
}, },
@ -49,6 +49,5 @@
], ],
"subtitle": "Inventory, Warehouses, Analysis and more.", "subtitle": "Inventory, Warehouses, Analysis and more.",
"success_message": "The Stock Module is all set up!", "success_message": "The Stock Module is all set up!",
"title": "Let's Setup the Stock Module.", "title": "Let's Set Up the Stock Module."
"user_can_dismiss": 1
} }

View File

@ -8,7 +8,7 @@
"is_mandatory": 0, "is_mandatory": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-14 22:09:10.043554", "modified": "2020-06-29 16:36:53.948242",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create a Supplier", "name": "Create a Supplier",
"owner": "Administrator", "owner": "Administrator",

View File

@ -120,6 +120,16 @@ class TransactionBase(StatusUpdater):
def validate_rate_with_reference_doc(self, ref_details): def validate_rate_with_reference_doc(self, ref_details):
buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]
selling_doctypes = ["Sales Invoice", "Delivery Note"]
if self.doctype in buying_doctypes:
to_disable = "Maintain same rate throughout Purchase cycle"
settings_page = "Buying Settings"
else:
to_disable = "Maintain same rate throughout Sales cycle"
settings_page = "Selling Settings"
for ref_dt, ref_dn_field, ref_link_field in ref_details: for ref_dt, ref_dn_field, ref_link_field in ref_details:
for d in self.get("items"): for d in self.get("items"):
if d.get(ref_link_field): if d.get(ref_link_field):
@ -129,8 +139,8 @@ class TransactionBase(StatusUpdater):
frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ") frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ")
.format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate)) .format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate))
frappe.throw(_("To allow different rates, disable the {0} checkbox in {1}.") frappe.throw(_("To allow different rates, disable the {0} checkbox in {1}.")
.format(frappe.bold(_("Maintain Same Rate Throughout Sales Cycle")), .format(frappe.bold(_(to_disable)),
get_link_to_form("Selling Settings", "Selling Settings", frappe.bold("Selling Settings")))) get_link_to_form(settings_page, settings_page, frappe.bold(settings_page))))
def get_link_filters(self, for_doctype): def get_link_filters(self, for_doctype):
if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype): if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype):