diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 31315e4c710..a2497838eed 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -147,10 +147,15 @@ "link_to": "Trial Balance", "type": "Report" }, + { + "label": "Point of Sale", + "link_to": "point-of-sale", + "type": "Page" + }, { "label": "Dashboard", "link_to": "Accounts", "type": "Dashboard" } ] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index 1bf9196a4f7..0e3b24cda3d 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -225,7 +225,7 @@ def build_tree_from_json(chart_template, chart_data=None): account['parent_account'] = parent account['expandable'] = True if identify_is_group(child) else False - account['value'] = (child.get('account_number') + ' - ' + account_name) \ + account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \ if child.get('account_number') else account_name accounts.append(account) _import_accounts(child, account['value']) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 8ca8b71ef8d..b2e8b090c7c 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -225,7 +225,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2020-06-22 20:13:26.043092", + "modified": "2020-08-03 20:13:26.043092", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 6fec3ab3681..76d82e73393 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -60,12 +60,12 @@ class BankClearance(Document): """.format(condition=condition), {"account": self.account, "from":self.from_date, "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) - pos_entries = [] + pos_sales_invoices, pos_purchase_invoices = [], [] if self.include_pos_transactions: - pos_entries = frappe.db.sql(""" + pos_sales_invoices = frappe.db.sql(""" select "Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit, - si.posting_date, si.debit_to as against_account, sip.clearance_date, + si.posting_date, si.customer as against_account, sip.clearance_date, account.account_currency, 0 as credit from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account where @@ -75,7 +75,20 @@ class BankClearance(Document): si.posting_date ASC, si.name DESC """, {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1) - entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)), + pos_purchase_invoices = frappe.db.sql(""" + select + "Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit, + pi.posting_date, pi.supplier as against_account, pi.clearance_date, + account.account_currency, 0 as debit + from `tabPurchase Invoice` pi, `tabAccount` account + where + pi.cash_bank_account=%(account)s and pi.docstatus=1 and account.name = pi.cash_bank_account + and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s + order by + pi.posting_date ASC, pi.name DESC + """, {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) + + entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)), key=lambda k: k['posting_date'] or getdate(nowdate())) self.set('payment_entries', []) diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py index 990b896fde3..3a0d4162ae7 100644 --- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -26,22 +26,22 @@ def test_create_test_data(): "item_group": "_Test Item Group", "item_name": "_Test Tesla Car", "apply_warehouse_wise_reorder_level": 0, - "warehouse":"_Test Warehouse - _TC", + "warehouse":"Stores - TCP1", "gst_hsn_code": "999800", "valuation_rate": 5000, "standard_rate":5000, "item_defaults": [{ - "company": "_Test Company", - "default_warehouse": "_Test Warehouse - _TC", + "company": "_Test Company with perpetual inventory", + "default_warehouse": "Stores - TCP1", "default_price_list":"_Test Price List", - "expense_account": "_Test Account Cost for Goods Sold - _TC", - "buying_cost_center": "_Test Cost Center - _TC", - "selling_cost_center": "_Test Cost Center - _TC", - "income_account": "Sales - _TC" + "expense_account": "Cost of Goods Sold - TCP1", + "buying_cost_center": "Main - TCP1", + "selling_cost_center": "Main - TCP1", + "income_account": "Sales - TCP1" }], "show_in_website": 1, "route":"-test-tesla-car", - "website_warehouse": "_Test Warehouse - _TC" + "website_warehouse": "Stores - TCP1" }) item.insert() # create test item price @@ -63,12 +63,12 @@ def test_create_test_data(): "items": [{ "item_code": "_Test Tesla Car" }], - "warehouse":"_Test Warehouse - _TC", + "warehouse":"Stores - TCP1", "coupon_code_based":1, "selling": 1, "rate_or_discount": "Discount Percentage", "discount_percentage": 30, - "company": "_Test Company", + "company": "_Test Company with perpetual inventory", "currency":"INR", "for_price_list":"_Test Price List" }) @@ -112,7 +112,10 @@ class TestCouponCode(unittest.TestCase): self.assertEqual(coupon_code.get("used"),0) def test_2_sales_order_with_coupon_code(self): - so = make_sales_order(customer="_Test Customer",selling_price_list="_Test Price List",item_code="_Test Tesla Car", rate=5000,qty=1, do_not_submit=True) + so = make_sales_order(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', + customer="_Test Customer", selling_price_list="_Test Price List", item_code="_Test Tesla Car", rate=5000,qty=1, + do_not_submit=True) + so = frappe.get_doc('Sales Order', so.name) # check item price before coupon code is applied self.assertEqual(so.items[0].rate, 5000) @@ -120,7 +123,7 @@ class TestCouponCode(unittest.TestCase): so.sales_partner='_Test Coupon Partner' so.save() # check item price after coupon code is applied - self.assertEqual(so.items[0].rate, 3500) + self.assertEqual(so.items[0].rate, 3500) so.submit() def test_3_check_coupon_code_used_after_so(self): diff --git a/erpnext/accounts/page/pos/__init__.py b/erpnext/accounts/doctype/dunning/__init__.py similarity index 100% rename from erpnext/accounts/page/pos/__init__.py rename to erpnext/accounts/doctype/dunning/__init__.py diff --git a/erpnext/accounts/doctype/dunning/dunning.js b/erpnext/accounts/doctype/dunning/dunning.js new file mode 100644 index 00000000000..9909c6c2ab0 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.js @@ -0,0 +1,162 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Dunning", { + setup: function (frm) { + frm.set_query("sales_invoice", () => { + return { + filters: { + docstatus: 1, + company: frm.doc.company, + outstanding_amount: [">", 0], + status: "Overdue" + }, + }; + }); + frm.set_query("income_account", () => { + return { + filters: { + company: frm.doc.company, + root_type: "Income", + is_group: 0 + } + }; + }); + }, + refresh: function (frm) { + frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1); + frm.set_df_property( + "sales_invoice", + "read_only", + frm.doc.__islocal ? 0 : 1 + ); + if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") { + frm.add_custom_button(__("Resolve"), () => { + frm.set_value("status", "Resolved"); + }); + } + if (frm.doc.docstatus === 1 && frm.doc.status !== "Resolved") { + frm.add_custom_button( + __("Payment"), + function () { + frm.events.make_payment_entry(frm); + },__("Create") + ); + frm.page.set_inner_btn_group_as_primary(__("Create")); + } + + if(frm.doc.docstatus > 0) { + frm.add_custom_button(__('Ledger'), function() { + frappe.route_options = { + "voucher_no": frm.doc.name, + "from_date": frm.doc.posting_date, + "to_date": frm.doc.posting_date, + "company": frm.doc.company, + "show_cancelled_entries": frm.doc.docstatus === 2 + }; + frappe.set_route("query-report", "General Ledger"); + }, __('View')); + } + }, + overdue_days: function (frm) { + frappe.db.get_value( + "Dunning Type", + { + start_day: ["<", frm.doc.overdue_days], + end_day: [">=", frm.doc.overdue_days], + }, + "dunning_type", + (r) => { + if (r) { + frm.set_value("dunning_type", r.dunning_type); + } else { + frm.set_value("dunning_type", ""); + frm.set_value("rate_of_interest", ""); + frm.set_value("dunning_fee", ""); + } + } + ); + }, + dunning_type: function (frm) { + frm.trigger("get_dunning_letter_text"); + }, + language: function (frm) { + frm.trigger("get_dunning_letter_text"); + }, + get_dunning_letter_text: function (frm) { + if (frm.doc.dunning_type) { + frappe.call({ + method: + "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text", + args: { + dunning_type: frm.doc.dunning_type, + language: frm.doc.language, + doc: frm.doc, + }, + callback: function (r) { + if (r.message) { + frm.set_value("body_text", r.message.body_text); + frm.set_value("closing_text", r.message.closing_text); + frm.set_value("language", r.message.language); + } else { + frm.set_value("body_text", ""); + frm.set_value("closing_text", ""); + } + }, + }); + } + }, + due_date: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + posting_date: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + rate_of_interest: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + outstanding_amount: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + interest_amount: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + dunning_fee: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + sales_invoice: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + calculate_overdue_days: function (frm) { + if (frm.doc.posting_date && frm.doc.due_date) { + const overdue_days = moment(frm.doc.posting_date).diff( + frm.doc.due_date, + "days" + ); + frm.set_value("overdue_days", overdue_days); + } + }, + calculate_interest_and_amount: function (frm) { + const interest_per_year = frm.doc.outstanding_amount * frm.doc.rate_of_interest / 100; + const interest_amount = flt((interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0, precision('interest_amount')); + const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision('dunning_amount')); + const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision('grand_total')); + frm.set_value("interest_amount", interest_amount); + frm.set_value("dunning_amount", dunning_amount); + frm.set_value("grand_total", grand_total); + }, + make_payment_entry: function (frm) { + return frappe.call({ + method: + "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", + args: { + dt: frm.doc.doctype, + dn: frm.doc.name, + }, + callback: function (r) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + }, + }); + }, +}); diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json new file mode 100644 index 00000000000..d55bfd1ac4c --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.json @@ -0,0 +1,370 @@ +{ + "actions": [], + "allow_events_in_timeline": 1, + "autoname": "naming_series:", + "creation": "2019-07-05 16:34:31.013238", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "title", + "naming_series", + "sales_invoice", + "customer", + "customer_name", + "outstanding_amount", + "currency", + "conversion_rate", + "column_break_3", + "company", + "posting_date", + "posting_time", + "due_date", + "overdue_days", + "address_and_contact_section", + "address_display", + "contact_display", + "contact_mobile", + "contact_email", + "column_break_18", + "company_address_display", + "section_break_6", + "dunning_type", + "dunning_fee", + "column_break_8", + "rate_of_interest", + "interest_amount", + "section_break_12", + "dunning_amount", + "grand_total", + "income_account", + "column_break_17", + "status", + "printing_setting_section", + "language", + "body_text", + "column_break_22", + "letter_head", + "closing_text", + "amended_from" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "default": "DUNN-.MM.-.YY.-", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "DUNN-.MM.-.YY.-" + }, + { + "fieldname": "sales_invoice", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sales Invoice", + "options": "Sales Invoice", + "reqd": 1 + }, + { + "fetch_from": "sales_invoice.customer_name", + "fieldname": "customer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Customer Name", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.outstanding_amount", + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "label": "Outstanding Amount", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "overdue_days", + "fieldtype": "Int", + "label": "Overdue Days", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "dunning_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Dunning Type", + "options": "Dunning Type", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "interest_amount", + "fieldtype": "Currency", + "label": "Interest Amount", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "dunning_type.dunning_fee", + "fetch_if_empty": 1, + "fieldname": "dunning_fee", + "fieldtype": "Currency", + "label": "Dunning Fee", + "precision": "2" + }, + { + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "printing_setting_section", + "fieldtype": "Section Break", + "label": "Printing Setting" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" + }, + { + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Dunning", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "{customer_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title" + }, + { + "fieldname": "body_text", + "fieldtype": "Text Editor", + "label": "Body Text" + }, + { + "fieldname": "closing_text", + "fieldtype": "Text Editor", + "label": "Closing Text" + }, + { + "fetch_from": "sales_invoice.due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date", + "read_only": 1 + }, + { + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time" + }, + { + "default": "0", + "fetch_from": "dunning_type.rate_of_interest", + "fetch_if_empty": 1, + "fieldname": "rate_of_interest", + "fieldtype": "Float", + "label": "Rate of Interest (%) Yearly" + }, + { + "fieldname": "address_and_contact_section", + "fieldtype": "Section Break", + "label": "Address and Contact" + }, + { + "fetch_from": "sales_invoice.address_display", + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Address", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_display", + "fieldname": "contact_display", + "fieldtype": "Small Text", + "label": "Contact", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_mobile", + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "label": "Mobile No", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.company_address_display", + "fieldname": "company_address_display", + "fieldtype": "Small Text", + "label": "Company Address", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_email", + "fieldname": "contact_email", + "fieldtype": "Data", + "label": "Contact Email", + "options": "Email", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.customer", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "grand_total", + "fieldtype": "Currency", + "label": "Grand Total", + "precision": "2", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "Unresolved", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "options": "Draft\nResolved\nUnresolved\nCancelled" + }, + { + "fieldname": "dunning_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Dunning Amount", + "read_only": 1 + }, + { + "fieldname": "income_account", + "fieldtype": "Link", + "label": "Income Account", + "options": "Account" + }, + { + "fetch_from": "sales_invoice.conversion_rate", + "fieldname": "conversion_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Rate", + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-08-03 18:55:43.683053", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "customer_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py new file mode 100644 index 00000000000..3e372affa12 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import json +from six import string_types +from frappe.utils import getdate, get_datetime, rounded, flt, cint +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year +from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.controllers.accounts_controller import AccountsController + + +class Dunning(AccountsController): + def validate(self): + self.validate_overdue_days() + self.validate_amount() + if not self.income_account: + self.income_account = frappe.db.get_value('Company', self.company, 'default_income_account') + + def validate_overdue_days(self): + self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0 + + def validate_amount(self): + amounts = calculate_interest_and_amount( + self.posting_date, self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days) + if self.interest_amount != amounts.get('interest_amount'): + self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount')) + if self.dunning_amount != amounts.get('dunning_amount'): + self.dunning_amount = flt(amounts.get('dunning_amount'), self.precision('dunning_amount')) + if self.grand_total != amounts.get('grand_total'): + self.grand_total = flt(amounts.get('grand_total'), self.precision('grand_total')) + + def on_submit(self): + self.make_gl_entries() + + def on_cancel(self): + if self.dunning_amount: + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) + + def make_gl_entries(self): + if not self.dunning_amount: + return + gl_entries = [] + invoice_fields = ["project", "cost_center", "debit_to", "party_account_currency", "conversion_rate", "cost_center"] + inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1) + + accounting_dimensions = get_accounting_dimensions() + invoice_fields.extend(accounting_dimensions) + + dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate) + default_cost_center = frappe.get_cached_value('Company', self.company, 'cost_center') + + gl_entries.append( + self.get_gl_dict({ + "account": inv.debit_to, + "party_type": "Customer", + "party": self.customer, + "due_date": self.due_date, + "against": self.income_account, + "debit": dunning_in_company_currency, + "debit_in_account_currency": self.dunning_amount, + "against_voucher": self.name, + "against_voucher_type": "Dunning", + "cost_center": inv.cost_center or default_cost_center, + "project": inv.project + }, inv.party_account_currency, item=inv) + ) + gl_entries.append( + self.get_gl_dict({ + "account": self.income_account, + "against": self.customer, + "credit": dunning_in_company_currency, + "cost_center": inv.cost_center or default_cost_center, + "credit_in_account_currency": self.dunning_amount, + "project": inv.project + }, item=inv) + ) + make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False) + + +def resolve_dunning(doc, state): + for reference in doc.references: + if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0: + dunnings = frappe.get_list('Dunning', filters={ + 'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}) + + for dunning in dunnings: + frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved') + +def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days): + interest_amount = 0 + if rate_of_interest: + interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100 + interest_amount = (interest_per_year * cint(overdue_days)) / 365 + grand_total = flt(outstanding_amount) + flt(interest_amount) + flt(dunning_fee) + dunning_amount = flt(interest_amount) + flt(dunning_fee) + return { + 'interest_amount': interest_amount, + 'grand_total': grand_total, + 'dunning_amount': dunning_amount} + +@frappe.whitelist() +def get_dunning_letter_text(dunning_type, doc, language=None): + if isinstance(doc, string_types): + doc = json.loads(doc) + if language: + filters = {'parent': dunning_type, 'language': language} + else: + filters = {'parent': dunning_type, 'is_default_language': 1} + letter_text = frappe.db.get_value('Dunning Letter Text', filters, + ['body_text', 'closing_text', 'language'], as_dict=1) + if letter_text: + return { + 'body_text': frappe.render_template(letter_text.body_text, doc), + 'closing_text': frappe.render_template(letter_text.closing_text, doc), + 'language': letter_text.language + } diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py new file mode 100644 index 00000000000..19a73ddfa48 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'dunning', + 'non_standard_fieldnames': { + 'Journal Entry': 'reference_name', + 'Payment Entry': 'reference_name' + }, + 'transactions': [ + { + 'label': _('Payment'), + 'items': ['Payment Entry', 'Journal Entry'] + } + ] + } \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning/dunning_list.js b/erpnext/accounts/doctype/dunning/dunning_list.js new file mode 100644 index 00000000000..8dc0a8c8577 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning_list.js @@ -0,0 +1,9 @@ +frappe.listview_settings["Dunning"] = { + get_indicator: function (doc) { + if (doc.status === "Resolved") { + return [__("Resolved"), "green", "status,=,Resolved"]; + } else { + return [__("Unresolved"), "red", "status,=,Unresolved"]; + } + }, +}; diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py new file mode 100644 index 00000000000..cb18309e3c9 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/test_dunning.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.utils import add_days, today, nowdate +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice_against_cost_center +from erpnext.accounts.doctype.dunning.dunning import calculate_interest_and_amount +from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + +class TestDunning(unittest.TestCase): + @classmethod + def setUpClass(self): + create_dunning_type() + unlink_payment_on_cancel_of_invoice() + + @classmethod + def tearDownClass(self): + unlink_payment_on_cancel_of_invoice(0) + + def test_dunning(self): + dunning = create_dunning() + amounts = calculate_interest_and_amount( + dunning.posting_date, dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days) + self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44) + self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44) + self.assertEqual(round(amounts.get('grand_total'), 2), 120.44) + + def test_gl_entries(self): + dunning = create_dunning() + dunning.submit() + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s + order by account asc""", dunning.name, as_dict=1) + self.assertTrue(gl_entries) + expected_values = dict((d[0], d) for d in [ + ['Debtors - _TC', 20.44, 0.0], + ['Sales - _TC', 0.0, 20.44] + ]) + for gle in gl_entries: + self.assertEquals(expected_values[gle.account][0], gle.account) + self.assertEquals(expected_values[gle.account][1], gle.debit) + self.assertEquals(expected_values[gle.account][2], gle.credit) + + def test_payment_entry(self): + dunning = create_dunning() + dunning.submit() + pe = get_payment_entry("Dunning", dunning.name) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from_account_currency = dunning.currency + pe.paid_to_account_currency = dunning.currency + pe.source_exchange_rate = 1 + pe.target_exchange_rate = 1 + pe.insert() + pe.submit() + si_doc = frappe.get_doc('Sales Invoice', dunning.sales_invoice) + self.assertEqual(si_doc.outstanding_amount, 0) + + +def create_dunning(): + posting_date = add_days(today(), -20) + due_date = add_days(today(), -15) + sales_invoice = create_sales_invoice_against_cost_center( + posting_date=posting_date, due_date=due_date, status='Overdue') + dunning_type = frappe.get_doc("Dunning Type", 'First Notice') + dunning = frappe.new_doc("Dunning") + dunning.sales_invoice = sales_invoice.name + dunning.customer_name = sales_invoice.customer_name + dunning.outstanding_amount = sales_invoice.outstanding_amount + dunning.debit_to = sales_invoice.debit_to + dunning.currency = sales_invoice.currency + dunning.company = sales_invoice.company + dunning.posting_date = nowdate() + dunning.due_date = sales_invoice.due_date + dunning.dunning_type = 'First Notice' + dunning.rate_of_interest = dunning_type.rate_of_interest + dunning.dunning_fee = dunning_type.dunning_fee + dunning.save() + return dunning + +def create_dunning_type(): + dunning_type = frappe.new_doc("Dunning Type") + dunning_type.dunning_type = 'First Notice' + dunning_type.start_day = 10 + dunning_type.end_day = 20 + dunning_type.dunning_fee = 20 + dunning_type.rate_of_interest = 8 + dunning_type.append( + "dunning_letter_text", { + 'language': 'en', + 'body_text': 'We have still not received payment for our invoice ', + 'closing_text': 'We kindly request that you pay the outstanding amount immediately, including interest and late fees.' + } + ) + dunning_type.save() diff --git a/erpnext/healthcare/doctype/lab_test_groups/__init__.py b/erpnext/accounts/doctype/dunning_letter_text/__init__.py similarity index 100% rename from erpnext/healthcare/doctype/lab_test_groups/__init__.py rename to erpnext/accounts/doctype/dunning_letter_text/__init__.py diff --git a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json new file mode 100644 index 00000000000..5ede3a1071a --- /dev/null +++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json @@ -0,0 +1,70 @@ +{ + "actions": [], + "creation": "2019-12-06 04:25:40.215625", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "language", + "is_default_language", + "section_break_4", + "body_text", + "closing_text", + "section_break_7", + "body_and_closing_text_help" + ], + "fields": [ + { + "fieldname": "language", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Language", + "options": "Language" + }, + { + "default": "0", + "fieldname": "is_default_language", + "fieldtype": "Check", + "label": "Is Default Language" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "description": "Letter or Email Body Text", + "fieldname": "body_text", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Body Text" + }, + { + "description": "Letter or Email Closing Text", + "fieldname": "closing_text", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Closing Text" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "body_and_closing_text_help", + "fieldtype": "HTML", + "label": "Body and Closing Text Help", + "options": "
The fieldnames you can use in your template are the fields in the document. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.
" + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-14 18:02:35.988958", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning Letter Text", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py new file mode 100644 index 00000000000..426497b6072 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DunningLetterText(Document): + pass diff --git a/erpnext/healthcare/doctype/normal_test_items/__init__.py b/erpnext/accounts/doctype/dunning_type/__init__.py similarity index 100% rename from erpnext/healthcare/doctype/normal_test_items/__init__.py rename to erpnext/accounts/doctype/dunning_type/__init__.py diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.js b/erpnext/accounts/doctype/dunning_type/dunning_type.js new file mode 100644 index 00000000000..54156b488dd --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Dunning Type', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.json b/erpnext/accounts/doctype/dunning_type/dunning_type.json new file mode 100644 index 00000000000..da436644724 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.json @@ -0,0 +1,129 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:dunning_type", + "creation": "2019-12-04 04:59:08.003664", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "dunning_type", + "overdue_interval_section", + "start_day", + "column_break_4", + "end_day", + "section_break_6", + "dunning_fee", + "column_break_8", + "rate_of_interest", + "text_block_section", + "dunning_letter_text" + ], + "fields": [ + { + "fieldname": "dunning_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Dunning Type", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "dunning_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Dunning Fee" + }, + { + "description": "This section allows the user to set the Body and Closing text of the Dunning Letter for the Dunning Type based on language, which can be used in Print.", + "fieldname": "text_block_section", + "fieldtype": "Section Break", + "label": "Dunning Letter" + }, + { + "fieldname": "dunning_letter_text", + "fieldtype": "Table", + "options": "Dunning Letter Text" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "overdue_interval_section", + "fieldtype": "Section Break", + "label": "Overdue Interval" + }, + { + "fieldname": "start_day", + "fieldtype": "Int", + "label": "Start Day" + }, + { + "fieldname": "end_day", + "fieldtype": "Int", + "label": "End Day" + }, + { + "fieldname": "rate_of_interest", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rate of Interest (%) Yearly" + } + ], + "links": [], + "modified": "2020-07-15 17:14:17.835074", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.py b/erpnext/accounts/doctype/dunning_type/dunning_type.py new file mode 100644 index 00000000000..87087484289 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DunningType(Document): + pass diff --git a/erpnext/accounts/doctype/dunning_type/test_dunning_type.py b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py new file mode 100644 index 00000000000..b2fb26f34a5 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestDunningType(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 23ad1eef14c..479d4b64bb8 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -6,6 +6,7 @@ import unittest, frappe from frappe.utils import flt, nowdate from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.exceptions import InvalidAccountCurrency +from erpnext.accounts.general_ledger import StockAccountInvalidTransaction class TestJournalEntry(unittest.TestCase): def test_journal_entry_with_against_jv(self): @@ -81,19 +82,46 @@ class TestJournalEntry(unittest.TestCase): from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory set_perpetual_inventory() - jv = frappe.copy_doc(test_records[0]) + jv = frappe.copy_doc({ + "cheque_date": nowdate(), + "cheque_no": "33", + "company": "_Test Company with perpetual inventory", + "doctype": "Journal Entry", + "accounts": [ + { + "account": "Debtors - TCP1", + "party_type": "Customer", + "party": "_Test Customer", + "credit_in_account_currency": 400.0, + "debit_in_account_currency": 0.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + "cost_center": "Main - TCP1" + }, + { + "account": "_Test Bank - TCP1", + "credit_in_account_currency": 0.0, + "debit_in_account_currency": 400.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + "cost_center": "Main - TCP1" + } + ], + "naming_series": "_T-Journal Entry-", + "posting_date": nowdate(), + "user_remark": "test", + "voucher_type": "Bank Entry" + }) + jv.get("accounts")[0].update({ - "account": get_inventory_account('_Test Company'), - "company": "_Test Company", + "account": get_inventory_account('_Test Company with perpetual inventory'), + "company": "_Test Company with perpetual inventory", "party_type": None, "party": None }) - jv.insert() - - from erpnext.accounts.general_ledger import StockAccountInvalidTransaction self.assertRaises(StockAccountInvalidTransaction, jv.submit) - + jv.cancel() set_perpetual_inventory(0) def test_multi_currency(self): diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json index 597519858ae..4c1be6517cf 100644 --- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json +++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json @@ -1,426 +1,123 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-01-23 05:40:18.117583", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2018-01-23 05:40:18.117583", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loyalty_program", + "loyalty_program_tier", + "customer", + "invoice_type", + "invoice", + "redeem_against", + "loyalty_points", + "purchase_amount", + "expiry_date", + "posting_date", + "company" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loyalty Program", - "length": 0, - "no_copy": 0, - "options": "Loyalty Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loyalty_program", + "fieldtype": "Link", + "label": "Loyalty Program", + "options": "Loyalty Program" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_program_tier", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loyalty Program Tier", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loyalty_program_tier", + "fieldtype": "Data", + "label": "Loyalty Program Tier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_invoice", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Sales Invoice", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "redeem_against", + "fieldtype": "Link", + "label": "Redeem Against", + "options": "Loyalty Point Entry" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "redeem_against", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Redeem Against", - "length": 0, - "no_copy": 0, - "options": "Loyalty Point Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loyalty_points", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Loyalty Points" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_points", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Loyalty Points", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "purchase_amount", + "fieldtype": "Currency", + "label": "Purchase Amount" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "expiry_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expiry Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expiry_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expiry Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "invoice_type", + "fieldtype": "Link", + "label": "Invoice Type", + "options": "DocType" + }, + { + "fieldname": "invoice", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Invoice", + "options": "invoice_type" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 16:05:22.810347", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Loyalty Point Entry", - "name_case": "", - "owner": "Administrator", + ], + "in_create": 1, + "modified": "2020-01-30 17:27:55.964242", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Loyalty Point Entry", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User" } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "customer", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "customer", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py index d65a7d88e63..3579a1a9604 100644 --- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py +++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py @@ -18,7 +18,7 @@ def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=No date = today() return frappe.db.sql(''' - select name, loyalty_points, expiry_date, loyalty_program_tier, sales_invoice + select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice from `tabLoyalty Point Entry` where customer=%s and loyalty_program=%s and expiry_date>=%s and loyalty_points>0 and company=%s diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 563165b2cc8..cb753a3723d 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -36,7 +36,8 @@ def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=Non return {"loyalty_points": 0, "total_spent": 0} @frappe.whitelist() -def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0): +def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, \ + silent=False, include_expired_entry=False, current_transaction_amount=0): lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent) loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program) lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)) @@ -59,10 +60,10 @@ def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None if not loyalty_program: loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program") - if not (loyalty_program or silent): + if not loyalty_program and not silent: frappe.throw(_("Customer isn't enrolled in any Loyalty Program")) elif silent and not loyalty_program: - return frappe._dict({"loyalty_program": None}) + return frappe._dict({"loyalty_programs": None}) if not company: company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 341884c1901..ee73ccaa611 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -27,7 +27,7 @@ class TestLoyaltyProgram(unittest.TestCase): customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}) earned_points = get_points_earned(si_original) - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) @@ -42,8 +42,8 @@ class TestLoyaltyProgram(unittest.TestCase): earned_after_redemption = get_points_earned(si_redeem) - lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'redeem_against': lpe.name}) - lpe_earn = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) + lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name}) + lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption) self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points)) @@ -66,7 +66,7 @@ class TestLoyaltyProgram(unittest.TestCase): earned_points = get_points_earned(si_original) - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) @@ -82,8 +82,8 @@ class TestLoyaltyProgram(unittest.TestCase): customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}) earned_after_redemption = get_points_earned(si_redeem) - lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'redeem_against': lpe.name}) - lpe_earn = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) + lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name}) + lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption) self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points)) @@ -101,7 +101,7 @@ class TestLoyaltyProgram(unittest.TestCase): si.insert() si.submit() - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si.name, 'customer': si.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si.name, 'customer': si.customer}) self.assertEqual(True, not (lpe is None)) # cancelling sales invoice @@ -118,7 +118,7 @@ class TestLoyaltyProgram(unittest.TestCase): si_original.submit() earned_points = get_points_earned(si_original) - lpe_original = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe_original = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(lpe_original.loyalty_points, earned_points) # create sales invoice return @@ -130,10 +130,10 @@ class TestLoyaltyProgram(unittest.TestCase): si_return.submit() # fetch original invoice again as its status would have been updated - si_original = frappe.get_doc('Sales Invoice', lpe_original.sales_invoice) + si_original = frappe.get_doc('Sales Invoice', lpe_original.invoice) earned_points = get_points_earned(si_original) - lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(lpe_after_return.loyalty_points, earned_points) self.assertEqual(True, (lpe_original.loyalty_points > lpe_after_return.loyalty_points)) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 42c9fdeba46..adfaade36e4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Payment Entry', { }); frm.set_query("party_type", function() { return{ - "filters": { + filters: { "name": ["in", Object.keys(frappe.boot.party_account_types)], } } @@ -33,7 +33,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party_bank_account", function() { return { filters: { - "is_company_account":0, + is_company_account: 0, party_type: frm.doc.party_type, party: frm.doc.party } @@ -42,7 +42,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("bank_account", function() { return { filters: { - "is_company_account":1 + is_company_account: 1 } } }); @@ -90,7 +90,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("reference_doctype", "references", function() { if (frm.doc.party_type=="Customer") { - var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"]; + var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"]; } else if (frm.doc.party_type=="Supplier") { var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"]; } else if (frm.doc.party_type=="Employee") { @@ -125,7 +125,7 @@ frappe.ui.form.on('Payment Entry', { const child = locals[cdt][cdn]; const filters = {"docstatus": 1, "company": doc.company}; const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice', - 'Purchase Order', 'Expense Claim', 'Fees']; + 'Purchase Order', 'Expense Claim', 'Fees', 'Dunning']; if (in_list(party_type_doctypes, child.reference_doctype)) { filters[doc.party_type.toLowerCase()] = doc.party; @@ -342,7 +342,7 @@ frappe.ui.form.on('Payment Entry', { () => { frm.set_party_account_based_on_party = false; if (r.message.bank_account) { - frm.set_value("party_bank_account", r.message.bank_account); + frm.set_value("bank_account", r.message.bank_account); } } ]); @@ -863,10 +863,10 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Customer" && - !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype) + !in_list(["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"], row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "reference_doctype", null); - frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx])); + frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning", [row.idx])); return false; } diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 1cecab74ef3..9df8655ccfb 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -199,8 +199,8 @@ class PaymentEntry(AccountsController): def validate_account_type(self, account, account_types): account_type = frappe.db.get_value("Account", account, "account_type") - if account_type not in account_types: - frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) + # if account_type not in account_types: + # frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) def set_exchange_rate(self): if self.paid_from and not self.source_exchange_rate: @@ -223,7 +223,7 @@ class PaymentEntry(AccountsController): if self.party_type == "Student": valid_reference_doctypes = ("Fees") elif self.party_type == "Customer": - valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry") + valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning") elif self.party_type == "Supplier": valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": @@ -897,6 +897,10 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre total_amount = ref_doc.get("grand_total") exchange_rate = 1 outstanding_amount = ref_doc.get("outstanding_amount") + if reference_doctype == "Dunning": + total_amount = ref_doc.get("dunning_amount") + exchange_rate = 1 + outstanding_amount = ref_doc.get("dunning_amount") elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: total_amount = ref_doc.get("total_amount") if ref_doc.multi_currency: @@ -907,7 +911,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre elif reference_doctype != "Journal Entry": if party_account_currency == company_currency: if ref_doc.doctype == "Expense Claim": - total_amount = ref_doc.total_sanctioned_amount + total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) elif ref_doc.doctype == "Employee Advance": total_amount = ref_doc.advance_amount else: @@ -925,8 +929,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre outstanding_amount = ref_doc.get("outstanding_amount") bill_no = ref_doc.get("bill_no") elif reference_doctype == "Expense Claim": - outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) \ - - flt(ref_doc.get("total_amount+reimbursed")) - flt(ref_doc.get("total_advance_amount")) + outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\ + - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount")) elif reference_doctype == "Employee Advance": outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount) else: @@ -951,7 +955,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) - if dt in ("Sales Invoice", "Sales Order"): + if dt in ("Sales Invoice", "Sales Order", "Dunning"): party_type = "Customer" elif dt in ("Purchase Invoice", "Purchase Order"): party_type = "Supplier" @@ -980,7 +984,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type - if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \ + if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: @@ -1006,6 +1010,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= elif dt == "Fees": grand_total = doc.grand_total outstanding_amount = doc.outstanding_amount + elif dt == "Dunning": + grand_total = doc.grand_total + outstanding_amount = doc.grand_total else: if party_account_currency == doc.company_currency: grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) @@ -1075,15 +1082,35 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount): pe.append('references', reference) else: - pe.append("references", { - 'reference_doctype': dt, - 'reference_name': dn, - "bill_no": doc.get("bill_no"), - "due_date": doc.get("due_date"), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'allocated_amount': outstanding_amount - }) + if dt == "Dunning": + pe.append("references", { + 'reference_doctype': 'Sales Invoice', + 'reference_name': doc.get('sales_invoice'), + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': doc.get('outstanding_amount'), + 'outstanding_amount': doc.get('outstanding_amount'), + 'allocated_amount': doc.get('outstanding_amount') + }) + pe.append("references", { + 'reference_doctype': dt, + 'reference_name': dn, + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': doc.get('dunning_amount'), + 'outstanding_amount': doc.get('dunning_amount'), + 'allocated_amount': doc.get('dunning_amount') + }) + else: + pe.append("references", { + 'reference_doctype': dt, + 'reference_name': dn, + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'allocated_amount': outstanding_amount + }) pe.setup_party_account_field() pe.set_missing_values() @@ -1172,4 +1199,4 @@ def make_payment_order(source_name, target_doc=None): }, target_doc, set_missing_values) - return doclist + return doclist \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 287e00f70fd..e93ec951fb0 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -140,9 +140,6 @@ class PaymentRequest(Document): }) def set_as_paid(self): - if frappe.session.user == "Guest": - frappe.set_user("Administrator") - payment_entry = self.create_payment_entry() self.make_invoice() @@ -254,7 +251,7 @@ class PaymentRequest(Document): if status in ["Authorized", "Completed"]: redirect_to = None - self.run_method("set_as_paid") + self.set_as_paid() # if shopping cart enabled and in session if (shopping_cart_settings.enabled and hasattr(frappe.local, "session") diff --git a/erpnext/healthcare/doctype/sensitivity_test_items/__init__.py b/erpnext/accounts/doctype/pos_closing_entry/__init__.py similarity index 100% rename from erpnext/healthcare/doctype/sensitivity_test_items/__init__.py rename to erpnext/accounts/doctype/pos_closing_entry/__init__.py diff --git a/erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html b/erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html similarity index 71% rename from erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html rename to erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html index 2412b071b96..983f49563cd 100644 --- a/erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html +++ b/erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html @@ -12,15 +12,15 @@| {{ _("Account") }} | {{ _("Rate") }} | {{ _("Amount") }} | |
|---|---|---|---|
| {{ d.account_head }} | {{ d.rate }} % | -{{ d.amount }} {{ currency.symbol }} | +{{ frappe.utils.fmt_money(d.amount, currency=currency) }} |
' + html + '
') - .get(0); - } - }); - - this.prepare_customer_mapper() - this.autocomplete_customers(); - - this.party_field.$input - .on('input', function (e) { - if(me.customers_mapper.length <= 1) { - me.prepare_customer_mapper(e.target.value); - } - me.party_field.awesomeplete.list = me.customers_mapper; - }) - .on('awesomplete-select', function (e) { - var customer = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - if (!customer) return; - // create customer link - if (customer.action) { - customer.action.apply(me); - return; - } - me.toggle_list_customer(false); - me.toggle_edit_button(true); - me.update_customer_data(customer); - me.refresh(); - me.set_focus(); - me.list_customers_btn.removeClass("view_customer"); - }) - .on('focus', function (e) { - $(e.target).val('').trigger('input'); - me.toggle_edit_button(false); - - if(me.frm.doc.items.length) { - me.toggle_list_customer(false) - me.toggle_item_cart(true) - } else { - me.toggle_list_customer(true) - me.toggle_item_cart(false) - } - }) - .on("awesomplete-selectcomplete", function (e) { - var item = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - // clear text input if item is action - if (item.action) { - $(this).val(""); - } - me.make_item_list(item.customer_name); - }); - }, - - prepare_customer_mapper: function(key) { - var me = this; - var customer_data = ''; - - if (key) { - key = key.toLowerCase().trim(); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*')); - - customer_data = $.grep(this.customers, function(data) { - contact = me.contacts[data.name]; - if(reg.test(data.name.toLowerCase()) - || reg.test(data.customer_name.toLowerCase()) - || (contact && reg.test(contact["phone"])) - || (contact && reg.test(contact["mobile_no"])) - || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ - return data; - } - }) - } else { - customer_data = this.customers; - } - - this.customers_mapper = []; - - customer_data.forEach(function (c, index) { - if(index < 30) { - contact = me.contacts[c.name]; - if(contact && !c['phone']) { - c["phone"] = contact["phone"]; - c["email_id"] = contact["email_id"]; - c["mobile_no"] = contact["mobile_no"]; - } - - me.customers_mapper.push({ - label: c.name, - value: c.name, - customer_name: c.customer_name, - customer_group: c.customer_group, - territory: c.territory, - phone: contact ? contact["phone"] : '', - mobile_no: contact ? contact["mobile_no"] : '', - email_id: contact ? contact["email_id"] : '', - searchtext: ['customer_name', 'customer_group', 'name', 'value', - 'label', 'email_id', 'phone', 'mobile_no'] - .map(key => c[key]).join(' ') - .toLowerCase() - }); - } else { - return; - } - }); - - this.customers_mapper.push({ - label: "" - + " " - + __("Create a new Customer") - + "", - value: 'is_action', - action: me.add_customer - }); - }, - - autocomplete_customers: function() { - this.party_field.awesomeplete.list = this.customers_mapper; - }, - - toggle_edit_button: function(flag) { - this.page.wrapper.find('.edit-customer-btn').toggle(flag); - }, - - toggle_list_customer: function(flag) { - this.list_customers.toggle(flag); - }, - - toggle_item_cart: function(flag) { - this.wrapper.find('.pos-bill-wrapper').toggle(flag); - }, - - add_customer: function() { - this.frm.doc.customer = ""; - this.update_customer(true); - this.numeric_keypad.show(); - }, - - update_customer: function (new_customer) { - var me = this; - - this.customer_doc = new frappe.ui.Dialog({ - 'title': 'Customer', - fields: [ - { - "label": __("Full Name"), - "fieldname": "full_name", - "fieldtype": "Data", - "reqd": 1 - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Email Id"), - "fieldname": "email_id", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("Contact Number"), - "fieldname": "phone", - "fieldtype": "Data" - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Address Name"), - "read_only": 1, - "fieldname": "name", - "fieldtype": "Data" - }, - { - "label": __("Address Line 1"), - "fieldname": "address_line1", - "fieldtype": "Data" - }, - { - "label": __("Address Line 2"), - "fieldname": "address_line2", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("City"), - "fieldname": "city", - "fieldtype": "Data" - }, - { - "label": __("State"), - "fieldname": "state", - "fieldtype": "Data" - }, - { - "label": __("ZIP Code"), - "fieldname": "pincode", - "fieldtype": "Data" - }, - { - "label": __("Customer POS Id"), - "fieldname": "customer_pos_id", - "fieldtype": "Data", - "hidden": 1 - } - ] - }) - this.customer_doc.show() - this.render_address_data() - - this.customer_doc.set_primary_action(__("Save"), function () { - me.make_offline_customer(new_customer); - me.pos_bill.show(); - me.list_customers.hide(); - }); - }, - - render_address_data: function() { - var me = this; - this.address_data = this.address[this.frm.doc.customer] || {}; - if(!this.address_data.email_id || !this.address_data.phone) { - this.address_data = this.contacts[this.frm.doc.customer]; - } - - this.customer_doc.set_values(this.address_data) - if(!this.customer_doc.fields_dict.full_name.$input.val()) { - this.customer_doc.set_value("full_name", this.frm.doc.customer) - } - - if(!this.customer_doc.fields_dict.customer_pos_id.value) { - this.customer_doc.set_value("customer_pos_id", frappe.datetime.now_datetime()) - } - }, - - get_address_from_localstorage: function() { - this.address_details = this.get_customers_details() - return this.address_details[this.frm.doc.customer] - }, - - make_offline_customer: function(new_customer) { - this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name; - this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value; - this.customer_details = this.get_customers_details(); - this.customer_details[this.frm.doc.customer] = this.get_prompt_details(); - this.party_field.$input.val(this.frm.doc.customer); - this.update_address_and_customer_list(new_customer) - this.autocomplete_customers(); - this.update_customer_in_localstorage() - this.update_customer_in_localstorage() - this.customer_doc.hide() - }, - - update_address_and_customer_list: function(new_customer) { - var me = this; - if(new_customer) { - this.customers_mapper.push({ - label: this.frm.doc.customer, - value: this.frm.doc.customer, - customer_group: "", - territory: "" - }); - } - - this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details()) - }, - - get_prompt_details: function() { - this.prompt_details = this.customer_doc.get_values(); - this.prompt_details['country'] = this.pos_profile_data.country; - this.prompt_details['territory'] = this.pos_profile_data["territory"]; - this.prompt_details['customer_group'] = this.pos_profile_data["customer_group"]; - this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value; - return JSON.stringify(this.prompt_details) - }, - - update_customer_data: function (doc) { - var me = this; - this.frm.doc.customer = doc.label || doc.name; - this.frm.doc.customer_name = doc.customer_name; - this.frm.doc.customer_group = doc.customer_group; - this.frm.doc.territory = doc.territory; - this.pos_bill.show(); - this.numeric_keypad.show(); - }, - - make_item_list: function (customer) { - var me = this; - if (!this.price_list) { - frappe.msgprint(__("Price List not found or disabled")); - return; - } - - me.item_timeout = null; - - var $wrap = me.wrapper.find(".item-list"); - me.wrapper.find(".item-list").empty(); - - if (this.items.length > 0) { - $.each(this.items, function(index, obj) { - let customer_price_list = me.customer_wise_price_list[customer]; - let item_price - if (customer && customer_price_list && customer_price_list[obj.name]) { - item_price = format_currency(customer_price_list[obj.name], me.frm.doc.currency); - } else { - item_price = format_currency(me.price_list_data[obj.name], me.frm.doc.currency); - } - if(index < me.page_len) { - $(frappe.render_template("pos_item", { - item_code: obj.name, - item_price: item_price, - item_name: obj.name === obj.item_name ? "" : obj.item_name, - item_image: obj.image, - item_stock: __('Stock Qty') + ": " + me.get_actual_qty(obj), - item_uom: obj.stock_uom, - color: frappe.get_palette(obj.item_name), - abbr: frappe.get_abbr(obj.item_name) - })).tooltip().appendTo($wrap); - } - }); - - $wrap.append(` -" - +__("Not items found")+"
").appendTo($wrap) - } - - if (this.items.length == 1 - && this.search_item.$input.val()) { - this.search_item.$input.val(""); - this.add_to_cart(); - } - }, - - get_items: function (item_code) { - // To search item as per the key enter - - var me = this; - this.item_serial_no = {}; - this.item_batch_no = {}; - - if (item_code) { - return $.grep(this.item_data, function (item) { - if (item.item_code == item_code) { - return true - } - }) - } - - this.items_list = this.apply_category(); - - key = this.search_item.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g, '\\$&'); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '[\\w*\\s*[a-zA-Z0-9]*]*')) - search_status = true - - if (key) { - return $.grep(this.items_list, function (item) { - if (search_status) { - if (me.batch_no_data[item.item_code] && - in_list(me.batch_no_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return me.item_batch_no[item.item_code] = me.search_item.$input.val() - } else if (me.serial_no_data[item.item_code] - && in_list(Object.keys(me.serial_no_data[item.item_code]), me.search_item.$input.val())) { - search_status = false; - me.item_serial_no[item.item_code] = [me.search_item.$input.val(), me.serial_no_data[item.item_code][me.search_item.$input.val()]] - return true - } else if (me.barcode_data[item.item_code] && - in_list(me.barcode_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return true; - } else if (reg.test(item.item_code.toLowerCase()) || (item.description && reg.test(item.description.toLowerCase())) || - reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) { - return true - } - } - }) - } else { - return this.items_list; - } - }, - - apply_category: function() { - var me = this; - category = this.selected_item_group || "All Item Groups"; - if(category == 'All Item Groups') { - return this.item_data - } else { - return this.item_data.filter(function(element, index, array){ - return element.item_group == category; - }); - } - }, - - bind_items_event: function() { - var me = this; - $(this.wrapper).on('click', '.pos-bill-item', function() { - $(me.wrapper).find('.pos-bill-item').removeClass('active'); - $(this).addClass('active'); - me.numeric_val = ""; - me.numeric_id = "" - me.item_code = $(this).attr("data-item-code"); - me.render_selected_item() - me.bind_qty_event() - me.update_rate() - $(me.wrapper).find(".selected-item").scrollTop(1000); - }) - }, - - bind_qty_event: function () { - var me = this; - - $(this.wrapper).on("change", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty); - me.update_value(); - }) - - $(this.wrapper).on("focusout", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty, true); - me.update_value(); - }) - - $(this.wrapper).find("[data-action='increase-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).find("[data-action='decrease-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).on("change", ".pos-item-disc", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var discount = $(this).val(); - if(discount > 100){ - discount = $(this).val(''); - frappe.show_alert({ - indicator: 'red', - message: __('Discount amount cannot be greater than 100%') - }); - me.update_discount(item_code, discount); - }else{ - me.update_discount(item_code, discount); - me.update_value(); - } - }) - }, - - bind_events: function() { - var me = this; - // if form is local then allow this function - // $(me.wrapper).find(".pos-item-wrapper").on("click", function () { - $(this.wrapper).on("click", ".pos-item-wrapper", function () { - me.item_code = ''; - me.customer_validate(); - if($(me.pos_bill).is(":hidden")) return; - - if (me.frm.doc.docstatus == 0) { - me.items = me.get_items($(this).attr("data-item-code")) - me.add_to_cart(); - me.clear_selected_row(); - } - }); - - me.bind_delete_event() - }, - - update_qty: function (item_code, qty, remove_zero_qty_items) { - var me = this; - this.items = this.get_items(item_code); - this.validate_serial_no() - this.set_item_details(item_code, "qty", qty, remove_zero_qty_items); - }, - - update_discount: function(item_code, discount) { - var me = this; - this.items = this.get_items(item_code); - this.set_item_details(item_code, "discount_percentage", discount); - }, - - update_rate: function () { - var me = this; - $(this.wrapper).on("change", ".pos-item-price", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - me.set_item_details(item_code, "rate", $(this).val()); - me.update_value() - }) - }, - - update_value: function() { - var me = this; - var fields = {qty: ".pos-item-qty", "discount_percentage": ".pos-item-disc", - "rate": ".pos-item-price", "amount": ".pos-amount"} - this.child_doc = this.get_child_item(this.item_code); - - if(me.child_doc.length) { - $.each(fields, function(key, field) { - $(me.selected_row).find(field).val(me.child_doc[0][key]) - }) - } else { - this.clear_selected_row(); - } - }, - - clear_selected_row: function() { - $(this.wrapper).find('.selected-item').empty(); - }, - - render_selected_item: function() { - this.child_doc = this.get_child_item(this.item_code); - $(this.wrapper).find('.selected-item').empty(); - if(this.child_doc.length) { - this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, - this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false; - this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) - $(this.wrapper).find('.selected-item').html(this.selected_row) - } - - $(this.selected_row).find('.form-control').click(function(){ - $(this).select(); - }) - }, - - get_child_item: function(item_code) { - var me = this; - return $.map(me.frm.doc.items, function(doc){ - if(doc.item_code == item_code) { - return doc - } - }) - }, - - set_item_details: function (item_code, field, value, remove_zero_qty_items) { - var me = this; - if (value < 0) { - frappe.throw(__("Enter value must be positive")); - } - - this.remove_item = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == item_code) { - if (d.serial_no && field == 'qty') { - me.validate_serial_no_qty(d, item_code, field, value) - } - - d[field] = flt(value); - d.amount = flt(d.rate) * flt(d.qty); - if (d.qty == 0 && remove_zero_qty_items) { - me.remove_item.push(d.idx) - } - - if(field=="discount_percentage" && value == 0) { - d.rate = d.price_list_rate; - } - } - }); - - if (field == 'qty') { - this.remove_zero_qty_items_from_cart(); - } - - this.update_paid_amount_status(false) - }, - - remove_zero_qty_items_from_cart: function () { - var me = this; - var idx = 0; - this.items = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (!in_list(me.remove_item, d.idx)) { - d.idx = idx; - me.items.push(d); - idx++; - } - }); - - this.frm.doc["items"] = this.items; - }, - - make_discount_field: function () { - var me = this; - - this.wrapper.find('input.discount-percentage').on("change", function () { - me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage")); - - if(me.frm.doc.additional_discount_percentage && me.frm.doc.discount_amount) { - // Reset discount amount - me.frm.doc.discount_amount = 0; - } - - var total = me.frm.doc.grand_total - - if (me.frm.doc.apply_discount_on == 'Net Total') { - total = me.frm.doc.net_total - } - - me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount")); - me.refresh(); - me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount) - }); - - this.wrapper.find('input.discount-amount').on("change", function () { - me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount")); - me.frm.doc.additional_discount_percentage = 0.0; - me.refresh(); - me.wrapper.find('input.discount-percentage').val(0); - }); - }, - - customer_validate: function () { - var me = this; - if (!this.frm.doc.customer || this.party_field.get_value() == "") { - frappe.throw(__("Please select customer")) - } - }, - - add_to_cart: function () { - var me = this; - var caught = false; - var no_of_items = me.wrapper.find(".pos-bill-item").length; - - this.customer_validate(); - this.mandatory_batch_no(); - this.validate_serial_no(); - this.validate_warehouse(); - - if (no_of_items != 0) { - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == me.items[0].item_code) { - caught = true; - d.qty += 1; - d.amount = flt(d.rate) * flt(d.qty); - if (me.item_serial_no[d.item_code]) { - d.serial_no += '\n' + me.item_serial_no[d.item_code][0] - d.warehouse = me.item_serial_no[d.item_code][1] - } - - if (me.item_batch_no.length) { - d.batch_no = me.item_batch_no[d.item_code] - } - } - }); - } - - // if item not found then add new item - if (!caught) - this.add_new_item_to_grid(); - - this.update_paid_amount_status(false) - this.wrapper.find(".item-cart-items").scrollTop(1000); - }, - - add_new_item_to_grid: function () { - var me = this; - this.child = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + " Item", "items"); - this.child.item_code = this.items[0].item_code; - this.child.item_name = this.items[0].item_name; - this.child.stock_uom = this.items[0].stock_uom; - this.child.uom = this.items[0].sales_uom || this.items[0].stock_uom; - this.child.conversion_factor = this.items[0].conversion_factor || 1; - this.child.brand = this.items[0].brand; - this.child.description = this.items[0].description || this.items[0].item_name; - this.child.discount_percentage = 0.0; - this.child.qty = 1; - this.child.item_group = this.items[0].item_group; - this.child.cost_center = this.pos_profile_data['cost_center'] || this.items[0].cost_center; - this.child.income_account = this.pos_profile_data['income_account'] || this.items[0].income_account; - this.child.warehouse = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse)); - - customer = this.frm.doc.customer; - let rate; - - customer_price_list = this.customer_wise_price_list[customer] - if (customer_price_list && customer_price_list[this.child.item_code]){ - rate = flt(this.customer_wise_price_list[customer][this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - else{ - rate = flt(this.price_list_data[this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - - this.child.price_list_rate = rate; - this.child.rate = rate; - this.child.actual_qty = me.get_actual_qty(this.items[0]); - this.child.amount = flt(this.child.qty) * flt(this.child.rate); - this.child.batch_no = this.item_batch_no[this.child.item_code]; - this.child.serial_no = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][0] : ''); - this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]); - }, - - update_paid_amount_status: function (update_paid_amount) { - if (this.frm.doc.offline_pos_name) { - update_paid_amount = update_paid_amount ? false : true; - } - - this.refresh(update_paid_amount); - }, - - refresh: function (update_paid_amount) { - var me = this; - this.refresh_fields(update_paid_amount); - this.set_primary_action(); - }, - - refresh_fields: function (update_paid_amount) { - this.apply_pricing_rule(); - this.discount_amount_applied = false; - this._calculate_taxes_and_totals(); - this.calculate_discount_amount(); - this.show_items_in_item_cart(); - this.set_taxes(); - this.calculate_outstanding_amount(update_paid_amount); - this.set_totals(); - this.update_total_qty(); - }, - - get_company_currency: function () { - return erpnext.get_currency(this.frm.doc.company); - }, - - show_items_in_item_cart: function () { - var me = this; - var $items = this.wrapper.find(".items").empty(); - var $no_items_message = this.wrapper.find(".no-items-message"); - $no_items_message.toggle(this.frm.doc.items.length === 0); - - var $totals_area = this.wrapper.find('.totals-area'); - $totals_area.toggle(this.frm.doc.items.length > 0); - - $.each(this.frm.doc.items || [], function (i, d) { - $(frappe.render_template("pos_bill_item_new", { - item_code: d.item_code, - item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("| {{_(\\\"Description\\\")}} | \\n\\t{{_(\\\"Amount\\\")}} | \\n
|---|---|
| \\n {{_(\\\"Outstanding Amount\\\")}}\\n | \\n\\n {{doc.get_formatted(\\\"outstanding_amount\\\")}}\\n | \\n
| \\n {{_(\\\"Interest \\\")}} {{doc.rate_of_interest}}% p.a. ({{doc.overdue_days}} {{_(\\\"days\\\")}})\\n | \\n\\n {{doc.get_formatted(\\\"interest_amount\\\")}}\\n | \\n
| \\n {{_(\\\"Dunning Fee\\\")}}\\n | \\n\\n {{doc.get_formatted(\\\"dunning_fee\\\")}}\\n | \\n
\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", "idx": 0, "line_breaks": 0, - "modified": "2019-12-09 17:39:23.356573", + "modified": "2020-04-29 16:39:12.936215", "modified_by": "Administrator", "module": "Accounts", "name": "GST POS Invoice", diff --git a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json index be699228c52..13a973d2341 100644 --- a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json @@ -6,10 +6,10 @@ "doc_type": "Sales Invoice", "docstatus": 0, "doctype": "Print Format", - "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", "idx": 1, "line_breaks": 0, - "modified": "2019-12-09 17:40:53.183574", + "modified": "2020-04-29 16:35:07.043058", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 66aa18058b6..59117c81748 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -643,8 +643,10 @@ class ReceivablePayableReport(object): account_type = "Receivable" if self.party_type == "Customer" else "Payable" accounts = [d.name for d in frappe.get_all("Account", filters={"account_type": account_type, "company": self.filters.company})] - conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) - values += accounts + + if accounts: + conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) + values += accounts def add_customer_filters(self, conditions, values): if self.filters.get("customer_group"): diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 4e22b05a81d..2563b66d1cf 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -223,9 +223,9 @@ class GrossProfitGenerator(object): # IMP NOTE # stock_ledger_entries should already be filtered by item_code and warehouse and # sorted by posting_date desc, posting_time desc - if item_code in self.non_stock_items: + if item_code in self.non_stock_items and (row.project or row.cost_center): #Issue 6089-Get last purchasing rate for non-stock item - item_rate = self.get_last_purchase_rate(item_code) + item_rate = self.get_last_purchase_rate(item_code, row) return flt(row.qty) * item_rate else: @@ -253,38 +253,34 @@ class GrossProfitGenerator(object): def get_average_buying_rate(self, row, item_code): args = row if not item_code in self.average_buying_rate: - if item_code in self.non_stock_items: - self.average_buying_rate[item_code] = flt(frappe.db.sql(""" - select sum(base_net_amount) / sum(qty * conversion_factor) - from `tabPurchase Invoice Item` - where item_code = %s and docstatus=1""", item_code)[0][0]) - else: - args.update({ - 'voucher_type': row.parenttype, - 'voucher_no': row.parent, - 'allow_zero_valuation': True, - 'company': self.filters.company - }) + args.update({ + 'voucher_type': row.parenttype, + 'voucher_no': row.parent, + 'allow_zero_valuation': True, + 'company': self.filters.company + }) - average_buying_rate = get_incoming_rate(args) - self.average_buying_rate[item_code] = flt(average_buying_rate) + average_buying_rate = get_incoming_rate(args) + self.average_buying_rate[item_code] = flt(average_buying_rate) return self.average_buying_rate[item_code] - def get_last_purchase_rate(self, item_code): + def get_last_purchase_rate(self, item_code, row): + condition = '' + if row.project: + condition += " AND a.project='%s'" % (row.project) + elif row.cost_center: + condition += " AND a.cost_center='%s'" % (row.cost_center) if self.filters.to_date: - last_purchase_rate = frappe.db.sql(""" - select (a.base_rate / a.conversion_factor) - from `tabPurchase Invoice Item` a - where a.item_code = %s and a.docstatus=1 - and modified <= %s - order by a.modified desc limit 1""", (item_code, self.filters.to_date)) - else: - last_purchase_rate = frappe.db.sql(""" - select (a.base_rate / a.conversion_factor) - from `tabPurchase Invoice Item` a - where a.item_code = %s and a.docstatus=1 - order by a.modified desc limit 1""", item_code) + condition += " AND modified='%s'" % (self.filters.to_date) + + last_purchase_rate = frappe.db.sql(""" + select (a.base_rate / a.conversion_factor) + from `tabPurchase Invoice Item` a + where a.item_code = %s and a.docstatus=1 + {0} + order by a.modified desc limit 1""".format(condition), item_code) + return flt(last_purchase_rate[0][0]) if last_purchase_rate else 0 def load_invoice_items(self): @@ -321,7 +317,8 @@ class GrossProfitGenerator(object): `tabSales Invoice Item`.brand, `tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, - `tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return + `tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return, + `tabSales Invoice Item`.cost_center {sales_person_cols} from `tabSales Invoice` inner join `tabSales Invoice Item` diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 824b2f2efbf..51ac7cfbfac 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -122,7 +122,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company cost_center = frappe.form_dict.get("cost_center") - cond = [] + cond = ["is_cancelled=0"] if date: cond.append("posting_date <= %s" % frappe.db.escape(cstr(date))) else: @@ -206,7 +206,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company return flt(bal) def get_count_on(account, fieldname, date): - cond = [] + cond = ["is_cancelled=0"] if date: cond.append("posting_date <= %s" % frappe.db.escape(cstr(date))) else: diff --git a/erpnext/assets/assets_dashboard/asset/asset.json b/erpnext/assets/assets_dashboard/asset/asset.json new file mode 100644 index 00000000000..56b1e2a71ce --- /dev/null +++ b/erpnext/assets/assets_dashboard/asset/asset.json @@ -0,0 +1,39 @@ +{ + "cards": [ + { + "card": "Total Assets" + }, + { + "card": "New Assets (This Year)" + }, + { + "card": "Asset Value" + } + ], + "charts": [ + { + "chart": "Asset Value Analytics", + "width": "Full" + }, + { + "chart": "Category-wise Asset Value", + "width": "Half" + }, + { + "chart": "Location-wise Asset Value", + "width": "Half" + } + ], + "creation": "2020-07-14 18:23:53.343082", + "dashboard_name": "Asset", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2020-07-21 18:14:25.078929", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json new file mode 100644 index 00000000000..bc2edc9d7d6 --- /dev/null +++ b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json @@ -0,0 +1,27 @@ +{ + "chart_name": "Asset Value Analytics", + "chart_type": "Report", + "creation": "2020-07-14 18:23:53.091233", + "custom_options": "{\"type\": \"bar\", \"barOptions\": {\"stacked\": 1}, \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", + "filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2020-07-23 13:53:33.211371", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Value Analytics", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Fixed Asset Register", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json new file mode 100644 index 00000000000..e79d2d73722 --- /dev/null +++ b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "creation": "2020-07-14 18:23:53.146304", + "custom_options": "{\"type\": \"donut\", \"height\": 300, \"axisOptions\": {\"shortenYAxisNumbers\": 1}}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", + "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2020-07-23 13:39:32.429240", + "modified_by": "Administrator", + "module": "Assets", + "name": "Category-wise Asset Value", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Fixed Asset Register", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 0, + "x_field": "asset_category", + "y_axis": [ + { + "y_field": "asset_value" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json new file mode 100644 index 00000000000..481586e7ca9 --- /dev/null +++ b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "creation": "2020-07-14 18:23:53.195389", + "custom_options": "{\"type\": \"donut\", \"height\": 300, \"axisOptions\": {\"shortenYAxisNumbers\": 1}}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", + "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2020-07-23 13:42:44.912551", + "modified_by": "Administrator", + "module": "Assets", + "name": "Location-wise Asset Value", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Fixed Asset Register", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 0, + "x_field": "location", + "y_axis": [ + { + "y_field": "asset_value" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 97165a31d29..a3152abf205 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -7,8 +8,9 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ + "is_existing_asset", + "section_break_2", "naming_series", - "asset_name", "item_code", "item_name", "asset_category", @@ -17,29 +19,31 @@ "supplier", "customer", "image", - "purchase_invoice", + "journal_entry_for_scrap", "column_break_3", "company", + "asset_name", "location", "custodian", "department", - "purchase_date", "disposal_date", - "journal_entry_for_scrap", - "purchase_receipt", "accounting_dimensions_section", "cost_center", "dimension_col_break", - "section_break_5", - "gross_purchase_amount", + "purchase_details_section", + "purchase_receipt", + "purchase_invoice", "available_for_use_date", - "column_break_18", + "column_break_23", + "gross_purchase_amount", + "purchase_date", + "section_break_23", "calculate_depreciation", "allow_monthly_depreciation", - "is_existing_asset", + "column_break_33", "opening_accumulated_depreciation", "number_of_depreciations_booked", - "section_break_23", + "section_break_36", "finance_books", "section_break_33", "depreciation_method", @@ -64,7 +68,6 @@ "status", "booked_fixed_asset", "column_break_51", - "purchase_receipt_amount", "default_finance_book", "amended_from" @@ -187,6 +190,8 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", + "read_only": 1, + "read_only_depends_on": "eval:!doc.is_existing_asset", "reqd": 1 }, { @@ -204,25 +209,20 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, { "fieldname": "gross_purchase_amount", "fieldtype": "Currency", "label": "Gross Purchase Amount", "options": "Company:company:default_currency", + "read_only": 1, + "read_only_depends_on": "eval:!doc.is_existing_asset", "reqd": 1 }, { "fieldname": "available_for_use_date", "fieldtype": "Date", - "label": "Available-for-use Date" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" + "label": "Available-for-use Date", + "reqd": 1 }, { "default": "0", @@ -252,12 +252,14 @@ "no_copy": 1 }, { - "depends_on": "calculate_depreciation", + "collapsible": 1, + "collapsible_depends_on": "eval:doc.calculate_depreciation || doc.is_existing_asset", "fieldname": "section_break_23", "fieldtype": "Section Break", "label": "Depreciation" }, { + "columns": 10, "fieldname": "finance_books", "fieldtype": "Table", "label": "Finance Books", @@ -305,8 +307,7 @@ { "depends_on": "calculate_depreciation", "fieldname": "section_break_14", - "fieldtype": "Section Break", - "label": "Depreciation Schedule" + "fieldtype": "Section Break" }, { "fieldname": "schedules", @@ -456,12 +457,37 @@ "fieldname": "allow_monthly_depreciation", "fieldtype": "Check", "label": "Allow Monthly Depreciation" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "is_existing_asset", + "fieldname": "purchase_details_section", + "fieldtype": "Section Break", + "label": "Purchase Details" + }, + { + "fieldname": "column_break_23", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, + { + "depends_on": "calculate_depreciation", + "fieldname": "section_break_36", + "fieldtype": "Section Break" } ], "idx": 72, "image_field": "image", "is_submittable": 1, - "modified": "2019-10-22 15:47:36.050828", + "links": [], + "modified": "2020-07-28 15:04:44.452224", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/number_card/asset_value/asset_value.json b/erpnext/assets/number_card/asset_value/asset_value.json new file mode 100644 index 00000000000..68e5f54c789 --- /dev/null +++ b/erpnext/assets/number_card/asset_value/asset_value.json @@ -0,0 +1,21 @@ +{ + "aggregate_function_based_on": "value_after_depreciation", + "creation": "2020-07-14 18:23:53.302457", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Asset", + "filters_json": "[]", + "function": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Asset Value", + "modified": "2020-07-21 18:13:47.647997", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Value", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json b/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json new file mode 100644 index 00000000000..6c8fb356575 --- /dev/null +++ b/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json @@ -0,0 +1,20 @@ +{ + "creation": "2020-07-14 18:23:53.267919", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Asset", + "filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "New Assets (This Year)", + "modified": "2020-07-23 13:45:20.418766", + "modified_by": "Administrator", + "module": "Assets", + "name": "New Assets (This Year)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/assets/number_card/total_assets/total_assets.json b/erpnext/assets/number_card/total_assets/total_assets.json new file mode 100644 index 00000000000..d127de8f2c6 --- /dev/null +++ b/erpnext/assets/number_card/total_assets/total_assets.json @@ -0,0 +1,20 @@ +{ + "creation": "2020-07-14 18:23:53.233328", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Asset", + "filters_json": "[]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Assets", + "modified": "2020-07-21 18:12:51.664292", + "modified_by": "Administrator", + "module": "Assets", + "name": "Total Assets", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/buying/buying_dashboard/buying/buying.json b/erpnext/buying/buying_dashboard/buying/buying.json new file mode 100644 index 00000000000..ab7ebac1463 --- /dev/null +++ b/erpnext/buying/buying_dashboard/buying/buying.json @@ -0,0 +1,46 @@ +{ + "cards": [ + { + "card": "Annual Purchase" + }, + { + "card": "Purchase Orders to Receive" + }, + { + "card": "Purchase Orders to Bill" + }, + { + "card": "Active Suppliers" + } + ], + "charts": [ + { + "chart": "Purchase Order Trends", + "width": "Full" + }, + { + "chart": "Material Request Analysis", + "width": "Half" + }, + { + "chart": "Purchase Order Analysis", + "width": "Half" + }, + { + "chart": "Top Suppliers", + "width": "Full" + } + ], + "creation": "2020-07-20 21:01:02.541065", + "dashboard_name": "Buying", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 1, + "is_standard": 1, + "modified": "2020-07-22 12:48:38.112744", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/buying/dashboard_chart/material_request_analysis/material_request_analysis.json b/erpnext/buying/dashboard_chart/material_request_analysis/material_request_analysis.json new file mode 100644 index 00000000000..0b66f1f5e50 --- /dev/null +++ b/erpnext/buying/dashboard_chart/material_request_analysis/material_request_analysis.json @@ -0,0 +1,27 @@ +{ + "chart_name": "Material Request Analysis", + "chart_type": "Group By", + "creation": "2020-07-20 21:01:02.242563", + "custom_options": "{\"height\": 300}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Material Request", + "dynamic_filters_json": "[[\"Material Request\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Material Request\",\"status\",\"not in\",[\"Draft\",\"Cancelled\",\"Stopped\",null],false],[\"Material Request\",\"material_request_type\",\"=\",\"Purchase\",false],[\"Material Request\",\"docstatus\",\"=\",\"1\",false],[\"Material Request\",\"transaction_date\",\"Timespan\",\"last quarter\",false]]", + "group_by_based_on": "status", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 12:43:56.961250", + "modified": "2020-07-22 21:20:51.840194", + "modified_by": "Administrator", + "module": "Buying", + "name": "Material Request Analysis", + "number_of_groups": 0, + "owner": "Administrator", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/buying/dashboard_chart/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/dashboard_chart/purchase_order_analysis/purchase_order_analysis.json new file mode 100644 index 00000000000..020755b92dc --- /dev/null +++ b/erpnext/buying/dashboard_chart/purchase_order_analysis/purchase_order_analysis.json @@ -0,0 +1,24 @@ +{ + "chart_name": "Purchase Order Analysis", + "chart_type": "Report", + "creation": "2020-07-20 21:01:02.203880", + "custom_options": "{\"type\": \"donut\", \"height\": 300, \"axisOptions\": {\"shortenYAxisNumbers\": 1}}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -1)\",\"to_date\":\"frappe.datetime.nowdate()\"}", + "filters_json": "{\"group_by_po\":0}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:44:35.754973", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Analysis", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json b/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json new file mode 100644 index 00000000000..6452ed2139b --- /dev/null +++ b/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json @@ -0,0 +1,24 @@ +{ + "chart_name": "Purchase Order Trends", + "chart_type": "Report", + "creation": "2020-07-20 21:01:02.295012", + "custom_options": "{\"type\": \"line\", \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}, \"lineOptions\": {\"regionFill\": 1}}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}", + "filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Item\"}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-21 16:13:25.092287", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Trends", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Purchase Order Trends", + "timeseries": 0, + "type": "Line", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json b/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json new file mode 100644 index 00000000000..6f7da8ea870 --- /dev/null +++ b/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Top Suppliers", + "chart_type": "Report", + "creation": "2020-07-20 21:01:02.329519", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}", + "filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Supplier\"}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:43:40.829652", + "modified_by": "Administrator", + "module": "Buying", + "name": "Top Suppliers", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py deleted file mode 100644 index c6e2ffa634f..00000000000 --- a/erpnext/buying/dashboard_fixtures.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -import json -from frappe import _ -from frappe.utils import nowdate -from erpnext.accounts.dashboard_fixtures import _get_fiscal_year - -def get_data(): - - fiscal_year = _get_fiscal_year(nowdate()) - - if not fiscal_year: - return frappe._dict() - - company = frappe.get_doc("Company", get_company_for_dashboards()) - fiscal_year_name = fiscal_year.get("name") - start_date = str(fiscal_year.get("year_start_date")) - end_date = str(fiscal_year.get("year_end_date")) - - return frappe._dict({ - "dashboards": get_dashboards(), - "charts": get_charts(company, fiscal_year_name, start_date, end_date), - "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), - }) - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -def get_dashboards(): - return [{ - "name": "Buying", - "dashboard_name": "Buying", - "charts": [ - { "chart": "Purchase Order Trends", "width": "Full"}, - { "chart": "Material Request Analysis", "width": "Half"}, - { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Top Suppliers", "width": "Full"} - ], - "cards": [ - { "card": "Annual Purchase"}, - { "card": "Purchase Orders to Receive"}, - { "card": "Purchase Orders to Bill"}, - { "card": "Active Suppliers"} - ] - }] - -def get_charts(company, fiscal_year_name, start_date, end_date): - return [ - { - "name": "Purchase Order Analysis", - "chart_name": _("Purchase Order Analysis"), - "chart_type": "Report", - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }), - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name, - "from_date": start_date, - "to_date": end_date - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "type": "Donut" - }, - { - "name": "Material Request Analysis", - "chart_name": _("Material Request Analysis"), - "chart_type": "Group By", - "custom_options": json.dumps({"height": 300}), - "doctype": "Dashboard Chart", - "document_type": "Material Request", - "filters_json": json.dumps( - [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], - ["Material Request", "material_request_type", "=", "Purchase", False], - ["Material Request", "company", "=", company.name, False], - ["Material Request", "docstatus", "=", 1, False], - ["Material Request", "transaction_date", "Between", [start_date, end_date], False]] - ), - "group_by_based_on": "status", - "group_by_type": "Count", - "is_custom": 0, - "is_public": 1, - "number_of_groups": 0, - "owner": "Administrator", - "type": "Donut" - }, - { - "name": "Purchase Order Trends", - "chart_name": _("Purchase Order Trends"), - "chart_type": "Report", - "custom_options": json.dumps({ - "type": "line", - "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {}, - "lineOptions": { - "regionFill": 1 - } - }), - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name, - "period": "Monthly", - "fiscal_year": fiscal_year_name, - "period_based_on": "posting_date", - "based_on": "Item" - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Purchase Order Trends", - "type": "Line" - }, - { - "name": "Top Suppliers", - "chart_name": _("Top Suppliers"), - "chart_type": "Report", - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name, - "period": "Monthly", - "fiscal_year": fiscal_year_name, - "period_based_on": "posting_date", - "based_on": "Supplier" - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Purchase Receipt Trends", - "type": "Bar" - } - ] - -def get_number_cards(company, fiscal_year_name, start_date, end_date): - return [ - { - "name": "Annual Purchase", - "aggregate_function_based_on": "base_net_total", - "doctype": "Number Card", - "document_type": "Purchase Order", - "filters_json": json.dumps([ - ["Purchase Order", "transaction_date", "Between", [start_date, end_date], False], - ["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], - ["Purchase Order", "docstatus", "=", 1, False], - ["Purchase Order", "company", "=", company.name, False] - ]), - "function": "Sum", - "is_public": 1, - "label": _("Annual Purchase"), - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "name": "Purchase Orders to Receive", - "doctype": "Number Card", - "document_type": "Purchase Order", - "filters_json": json.dumps([ - ["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], - ["Purchase Order", "docstatus", "=", 1, False], - ["Purchase Order", "company", "=", company.name, False] - ]), - "function": "Count", - "is_public": 1, - "label": _("Purchase Orders to Receive"), - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Weekly" - }, - { - "name": "Purchase Orders to Bill", - "doctype": "Number Card", - "document_type": "Purchase Order", - "filters_json": json.dumps([ - ["Purchase Order", "status", "in", ["To Receive and Bill", "To Bill", None], False], - ["Purchase Order", "docstatus", "=", 1, False], - ["Purchase Order", "company", "=", company.name, False] - ]), - "function": "Count", - "is_public": 1, - "label": _("Purchase Orders to Bill"), - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Weekly" - }, - { - "name": "Active Suppliers", - "doctype": "Number Card", - "document_type": "Supplier", - "filters_json": json.dumps([["Supplier", "disabled", "=", "0"]]), - "function": "Count", - "is_public": 1, - "label": "Active Suppliers", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - } - ] \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 84e3a31904d..25065ab155f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -7,12 +7,6 @@ frappe.provide("erpnext.buying"); frappe.ui.form.on("Purchase Order", { setup: function(frm) { - frm.custom_make_buttons = { - 'Purchase Receipt': 'Receipt', - 'Purchase Invoice': 'Invoice', - 'Stock Entry': 'Material to Supplier', - 'Payment Entry': 'Payment' - } frm.set_query("reserve_warehouse", "supplied_items", function() { return { @@ -36,20 +30,6 @@ frappe.ui.form.on("Purchase Order", { }, - refresh: function(frm) { - if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed' - && flt(frm.doc.per_received) < 100 && flt(frm.doc.per_billed) < 100) { - frm.add_custom_button(__('Update Items'), () => { - erpnext.utils.update_child_items({ - frm: frm, - child_docname: "items", - child_doctype: "Purchase Order Detail", - cannot_add_row: false, - }) - }); - } - }, - onload: function(frm) { set_schedule_date(frm); if (!frm.doc.transaction_date){ @@ -76,6 +56,18 @@ frappe.ui.form.on("Purchase Order Item", { }); erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ + setup: function() { + this.frm.custom_make_buttons = { + 'Purchase Receipt': 'Receipt', + 'Purchase Invoice': 'Invoice', + 'Stock Entry': 'Material to Supplier', + 'Payment Entry': 'Payment', + } + + this._super(); + + }, + refresh: function(doc, cdt, cdn) { var me = this; this._super(); @@ -99,6 +91,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(doc.docstatus == 1) { if(!in_list(["Closed", "Delivered"], doc.status)) { + if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) { + this.frm.add_custom_button(__('Update Items'), () => { + erpnext.utils.update_child_items({ + frm: frm, + child_docname: "items", + child_doctype: "Purchase Order Detail", + cannot_add_row: false, + }) + }); + } if (this.frm.has_perm("submit")) { if(flt(doc.per_billed, 6) < 100 || flt(doc.per_received, 6) < 100) { if (doc.status != "On Hold") { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 13f5cb0c818..4201e0b6353 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -137,9 +137,7 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-user" }, { "allow_on_submit": 1, @@ -149,9 +147,7 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "naming_series", @@ -163,9 +159,7 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "bold": 1, @@ -178,18 +172,14 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests", - "show_days": 1, - "show_seconds": 1 + "label": "Get Items from Open Material Requests" }, { "bold": 1, @@ -198,9 +188,7 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "company", @@ -212,17 +200,13 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -234,35 +218,27 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By", - "show_days": 1, - "show_seconds": 1 + "label": "Required By" }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No", - "show_days": 1, - "show_seconds": 1 + "label": "Order Confirmation No" }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date", - "show_days": 1, - "show_seconds": 1 + "label": "Order Confirmation Date" }, { "fieldname": "amended_from", @@ -274,25 +250,19 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship", - "show_days": 1, - "show_seconds": 1 + "label": "Drop Ship" }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "bold": 1, @@ -300,41 +270,31 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact", - "show_days": 1, - "show_seconds": 1 + "options": "Contact" }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "customer_contact_email", @@ -342,60 +302,46 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact", - "show_days": 1, - "show_seconds": 1 + "label": "Address and Contact" }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_email", @@ -403,42 +349,32 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tag" }, { "fieldname": "currency", @@ -448,9 +384,7 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "conversion_rate", @@ -460,24 +394,18 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "price_list_currency", @@ -485,18 +413,14 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -505,15 +429,11 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -521,15 +441,11 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "No", @@ -538,33 +454,25 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-shopping-cart" }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode", - "show_days": 1, - "show_seconds": 1 + "label": "Scan Barcode" }, { "allow_bulk_edit": 1, @@ -574,34 +482,26 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules", - "show_days": 1, - "show_seconds": 1 + "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied", - "show_days": 1, - "show_seconds": 1 + "label": "Raw Materials Supplied" }, { "fieldname": "supplied_items", @@ -611,23 +511,17 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -635,9 +529,7 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_net_total", @@ -648,24 +540,18 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "net_total", @@ -675,26 +561,20 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "taxes_and_charges", @@ -703,30 +583,22 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_52", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "taxes", @@ -734,17 +606,13 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Purchase Taxes and Charges" }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup", - "show_days": 1, - "show_seconds": 1 + "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", @@ -753,17 +621,13 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "base_taxes_and_charges_added", @@ -773,9 +637,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -785,9 +647,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -798,15 +658,11 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "taxes_and_charges_added", @@ -816,9 +672,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -828,9 +682,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_taxes_and_charges", @@ -838,18 +690,14 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount", - "show_days": 1, - "show_seconds": 1 + "label": "Additional Discount" }, { "default": "Grand Total", @@ -857,9 +705,7 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_discount_amount", @@ -867,38 +713,28 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "totals_section", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "base_grand_total", @@ -909,9 +745,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_rounding_adjustment", @@ -920,21 +754,18 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", + "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -944,16 +775,12 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Column Break" }, { "fieldname": "grand_total", @@ -963,9 +790,7 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "rounding_adjustment", @@ -974,37 +799,30 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total", - "show_days": 1, - "show_seconds": 1 + "label": "Disable Rounded Total" }, { "fieldname": "in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "advance_paid", @@ -1013,25 +831,19 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms", - "show_days": 1, - "show_seconds": 1 + "label": "Payment Terms" }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Terms Template" }, { "fieldname": "payment_schedule", @@ -1039,9 +851,7 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1050,9 +860,7 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-legal" }, { "fieldname": "tc_name", @@ -1061,27 +869,21 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Text Editor" }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "default": "Draft", @@ -1096,9 +898,7 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "ref_sq", @@ -1109,9 +909,7 @@ "oldfieldname": "ref_sq", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "party_account_currency", @@ -1121,24 +919,18 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:!doc.__islocal", @@ -1148,9 +940,7 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -1160,9 +950,7 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1172,8 +960,6 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1184,9 +970,7 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1198,15 +982,11 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "allow_on_submit": 1, @@ -1214,25 +994,19 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", - "show_days": 1, - "show_seconds": 1 + "label": "Subscription Section" }, { "allow_on_submit": 1, @@ -1240,9 +1014,7 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1250,15 +1022,11 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "auto_repeat", @@ -1267,72 +1035,56 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference", - "show_days": 1, - "show_seconds": 1 + "label": "Update Auto Repeat Reference" }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category", - "show_days": 1, - "show_seconds": 1 + "options": "Tax Category" }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking", - "show_days": 1, - "show_seconds": 1 + "label": "Tracking" }, { "fieldname": "column_break_75", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "billing_address", "fieldtype": "Link", "label": "Select Billing Address", - "options": "Address", - "show_days": 1, - "show_seconds": 1 + "options": "Address" }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", "label": "Billing Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-06-13 22:25:47.333850", + "modified": "2020-07-31 14:13:44.610190", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1376,12 +1128,6 @@ "read": 1, "role": "Purchase Manager", "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts User" } ], "search_fields": "status, transaction_date, supplier,grand_total", @@ -1389,5 +1135,5 @@ "sort_field": "modified", "sort_order": "DESC", "timeline_field": "supplier", - "title_field": "title" + "title_field": "supplier" } \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 4606395ebe5..40362b1d404 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -97,7 +97,7 @@ { "fieldname": "default_bank_account", "fieldtype": "Link", - "label": "Default Bank Account", + "label": "Default Company Bank Account", "options": "Bank Account" }, { @@ -384,7 +384,7 @@ "idx": 370, "image_field": "image", "links": [], - "modified": "2020-03-17 09:48:30.578242", + "modified": "2020-06-17 23:18:20", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 7db1516ce1b..660dcff34bc 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -593,6 +593,7 @@ "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", + "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, @@ -642,6 +643,7 @@ "fieldname": "in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, @@ -803,7 +805,7 @@ "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-05-15 21:24:12.639482", + "modified": "2020-07-18 05:10:45.556792", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/number_card/active_suppliers/active_suppliers.json b/erpnext/buying/number_card/active_suppliers/active_suppliers.json new file mode 100644 index 00000000000..91d5b13b06f --- /dev/null +++ b/erpnext/buying/number_card/active_suppliers/active_suppliers.json @@ -0,0 +1,20 @@ +{ + "creation": "2020-07-20 21:01:02.499689", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Supplier", + "filters_json": "[[\"Supplier\",\"disabled\",\"=\",\"0\"]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Active Suppliers", + "modified": "2020-07-22 12:48:23.295193", + "modified_by": "Administrator", + "module": "Buying", + "name": "Active Suppliers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/buying/number_card/annual_purchase/annual_purchase.json b/erpnext/buying/number_card/annual_purchase/annual_purchase.json new file mode 100644 index 00000000000..79f1b653887 --- /dev/null +++ b/erpnext/buying/number_card/annual_purchase/annual_purchase.json @@ -0,0 +1,22 @@ +{ + "aggregate_function_based_on": "base_net_total", + "creation": "2020-07-20 21:01:02.367127", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Purchase Order", + "dynamic_filters_json": "[[\"Purchase Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Purchase Order\",\"transaction_date\",\"Timespan\",\"this year\",false],[\"Purchase Order\",\"status\",\"not in\",[\"Draft\",\"Cancelled\",\"Closed\",null],false],[\"Purchase Order\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Annual Purchase", + "modified": "2020-07-22 21:21:58.755188", + "modified_by": "Administrator", + "module": "Buying", + "name": "Annual Purchase", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/buying/number_card/purchase_orders_to_bill/purchase_orders_to_bill.json b/erpnext/buying/number_card/purchase_orders_to_bill/purchase_orders_to_bill.json new file mode 100644 index 00000000000..44a04563906 --- /dev/null +++ b/erpnext/buying/number_card/purchase_orders_to_bill/purchase_orders_to_bill.json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-20 21:01:02.468514", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Purchase Order", + "dynamic_filters_json": "[[\"Purchase Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Purchase Order\",\"status\",\"in\",[\"To Receive and Bill\",\"To Bill\",null],false],[\"Purchase Order\",\"docstatus\",\"=\",1,false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Purchase Orders to Bill", + "modified": "2020-07-22 12:48:10.300711", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Orders to Bill", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/buying/number_card/purchase_orders_to_receive/purchase_orders_to_receive.json b/erpnext/buying/number_card/purchase_orders_to_receive/purchase_orders_to_receive.json new file mode 100644 index 00000000000..442dab4b0c7 --- /dev/null +++ b/erpnext/buying/number_card/purchase_orders_to_receive/purchase_orders_to_receive.json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-20 21:01:02.438012", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Purchase Order", + "dynamic_filters_json": "[[\"Purchase Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Purchase Order\",\"status\",\"in\",[\"To Receive and Bill\",\"To Receive\",null],false],[\"Purchase Order\",\"docstatus\",\"=\",1,false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Purchase Orders to Receive", + "modified": "2020-07-22 12:47:47.460080", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Orders to Receive", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 90c67f1e521..3f127a201ef 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -213,7 +213,7 @@ def make_return_doc(doctype, source_name, target_doc=None): doc.return_against = source.name doc.ignore_pricing_rule = 1 doc.set_warehouse = "" - if doctype == "Sales Invoice": + if doctype == "Sales Invoice" or doctype == "POS Invoice": doc.is_pos = source.is_pos # look for Print Heading "Credit Note" @@ -229,7 +229,7 @@ def make_return_doc(doctype, source_name, target_doc=None): tax.tax_amount = -1 * tax.tax_amount if doc.get("is_return"): - if doc.doctype == 'Sales Invoice': + if doc.doctype == 'Sales Invoice' or doc.doctype == 'POS Invoice': doc.set('payments', []) for data in source.payments: paid_amount = 0.00 @@ -241,8 +241,11 @@ def make_return_doc(doctype, source_name, target_doc=None): 'mode_of_payment': data.mode_of_payment, 'type': data.type, 'amount': -1 * paid_amount, - 'base_amount': -1 * base_paid_amount + 'base_amount': -1 * base_paid_amount, + 'account': data.account }) + if doc.is_pos: + doc.paid_amount = -1 * source.paid_amount elif doc.doctype == 'Purchase Invoice': doc.paid_amount = -1 * source.paid_amount doc.base_paid_amount = -1 * source.base_paid_amount @@ -287,7 +290,7 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.dn_detail = source_doc.name if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return - elif doctype == "Sales Invoice": + elif doctype == "Sales Invoice" or doctype == "POS Invoice": target_doc.sales_order = source_doc.sales_order target_doc.delivery_note = source_doc.delivery_note target_doc.so_detail = source_doc.so_detail diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index b465a106f0e..0dc9878afd0 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -85,6 +85,12 @@ status_map = { "Bank Transaction": [ ["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"], ["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"] + ], + "POS Opening Entry": [ + ["Draft", None], + ["Open", "eval:self.docstatus == 1 and not self.pos_closing_entry"], + ["Closed", "eval:self.docstatus == 1 and self.pos_closing_entry"], + ["Cancelled", "eval:self.docstatus == 2"], ] } diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index a9eb9963bfe..572e1ca2397 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -370,7 +370,7 @@ class calculate_taxes_and_totals(object): self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"]) - if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]: + if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]: self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \ if self.doc.total_taxes_and_charges else self.doc.base_net_total else: @@ -597,7 +597,7 @@ class calculate_taxes_and_totals(object): base_rate_with_margin = 0.0 if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: - for d in item.pricing_rules.split(','): + for d in json.loads(item.pricing_rules): pricing_rule = frappe.get_cached_doc('Pricing Rule', d) if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\ @@ -619,17 +619,14 @@ class calculate_taxes_and_totals(object): self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) def update_paid_amount_for_return(self, total_amount_to_pay): - default_mode_of_payment = frappe.db.get_value('Sales Invoice Payment', - {'parent': self.doc.pos_profile, 'default': 1}, - ['mode_of_payment', 'type', 'account'], as_dict=1) + default_mode_of_payment = frappe.db.get_value('POS Payment Method', + {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) self.doc.payments = [] if default_mode_of_payment: self.doc.append('payments', { 'mode_of_payment': default_mode_of_payment.mode_of_payment, - 'type': default_mode_of_payment.type, - 'account': default_mode_of_payment.account, 'amount': total_amount_to_pay }) else: diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json index 736a9d61736..0340364bd52 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.json +++ b/erpnext/crm/doctype/email_campaign/email_campaign.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:MAIL-CAMP-{YYYY}-{#####}", "creation": "2019-06-30 16:05:30.015615", "doctype": "DocType", @@ -52,7 +53,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Email Campaign For ", - "options": "\nLead\nContact", + "options": "\nLead\nContact\nEmail Group", "reqd": 1 }, { @@ -70,7 +71,8 @@ "options": "User" } ], - "modified": "2019-11-11 17:18:47.342839", + "links": [], + "modified": "2020-07-15 12:43:25.548682", "modified_by": "Administrator", "module": "CRM", "name": "Email Campaign", diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py index 8f60ecf6219..71c93e8d393 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.py +++ b/erpnext/crm/doctype/email_campaign/email_campaign.py @@ -70,10 +70,15 @@ def send_email_to_leads_or_contacts(): send_mail(entry, email_campaign) def send_mail(entry, email_campaign): - recipient = frappe.db.get_value(email_campaign.email_campaign_for, email_campaign.get("recipient"), 'email_id') + recipient_list = [] + if email_campaign.email_campaign_for == "Email Group": + for member in frappe.db.get_list("Email Group Member", filters={"email_group": email_campaign.get("recipient")}, fields=["email"]): + recipient_list.append(member['email']) + else: + recipient_list.append(frappe.db.get_value(email_campaign.email_campaign_for, email_campaign.get("recipient"), "email_id")) email_template = frappe.get_doc("Email Template", entry.get("email_template")) - sender = frappe.db.get_value("User", email_campaign.get("sender"), 'email') + sender = frappe.db.get_value("User", email_campaign.get("sender"), "email") context = {"doc": frappe.get_doc(email_campaign.email_campaign_for, email_campaign.recipient)} # send mail and link communication to document comm = make( @@ -82,7 +87,7 @@ def send_mail(entry, email_campaign): subject = frappe.render_template(email_template.get("subject"), context), content = frappe.render_template(email_template.get("response"), context), sender = sender, - recipients = recipient, + recipients = recipient_list, communication_medium = "Email", sent_or_received = "Sent", send_email = True, diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 7bae8035080..08958b7dd65 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -30,7 +30,6 @@ frappe.ui.form.on("Opportunity", { }, party_name: function(frm) { - frm.toggle_display("contact_info", frm.doc.party_name); frm.trigger('set_contact_link'); if (frm.doc.opportunity_from == "Customer") { @@ -48,10 +47,6 @@ frappe.ui.form.on("Opportunity", { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); }, - with_items: function(frm) { - frm.trigger('toggle_mandatory'); - }, - customer_address: function(frm, cdt, cdn) { erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false); }, @@ -59,15 +54,19 @@ frappe.ui.form.on("Opportunity", { contact_person: erpnext.utils.get_contact_details, opportunity_from: function(frm) { + frm.trigger('setup_opportunity_from'); + + frm.set_value("party_name", ""); + }, + + setup_opportunity_from: function(frm) { frm.trigger('setup_queries'); - frm.toggle_reqd("party_name", frm.doc.opportunity_from); frm.trigger("set_dynamic_field_label"); }, refresh: function(frm) { var doc = frm.doc; - frm.events.opportunity_from(frm); - frm.trigger('toggle_mandatory'); + frm.trigger('setup_opportunity_from'); erpnext.toggle_naming_series(); if(!doc.__islocal && doc.status!=="Lost") { @@ -118,7 +117,6 @@ frappe.ui.form.on("Opportunity", { }, set_dynamic_field_label: function(frm){ - if (frm.doc.opportunity_from) { frm.set_df_property("party_name", "label", frm.doc.opportunity_from); } @@ -138,9 +136,6 @@ frappe.ui.form.on("Opportunity", { }) }, - toggle_mandatory: function(frm) { - frm.toggle_reqd("items", frm.doc.with_items ? 1:0); - } }) // TODO commonify this code diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 6a54c5fc6ef..545e2324acf 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -254,6 +254,7 @@ "fieldname": "items", "fieldtype": "Table", "label": "Items", + "mandatory_depends_on": "eval: doc.with_items == 1", "oldfieldname": "enquiry_details", "oldfieldtype": "Table", "options": "Opportunity Item" @@ -423,7 +424,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-04-07 09:05:39.391109", + "modified": "2020-07-14 16:49:15.888503", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/education/api.py b/erpnext/education/api.py index 1a19716b508..bf9f2215f32 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -104,6 +104,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None, student_attendance.date = date student_attendance.status = status student_attendance.save() + student_attendance.submit() @frappe.whitelist() @@ -151,7 +152,7 @@ def get_fee_components(fee_structure): :param fee_structure: Fee Structure. """ if fee_structure: - fs = frappe.get_list("Fee Component", fields=["fees_category", "amount"] , filters={"parent": fee_structure}, order_by= "idx") + fs = frappe.get_list("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx") return fs @@ -363,9 +364,9 @@ def get_current_enrollment(student, academic_year=None): select name as program_enrollment, student_name, program, student_batch_name as student_batch, student_category, academic_term, academic_year - from + from `tabProgram Enrollment` - where + where student = %s and academic_year = %s order by creation''', (student, current_academic_year), as_dict=1) diff --git a/erpnext/education/doctype/academic_term/academic_term_dashboard.py b/erpnext/education/doctype/academic_term/academic_term_dashboard.py new file mode 100644 index 00000000000..871e0f32845 --- /dev/null +++ b/erpnext/education/doctype/academic_term/academic_term_dashboard.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'academic_term', + 'transactions': [ + { + 'label': _('Student'), + 'items': ['Student Applicant', 'Student Group', 'Student Log'] + }, + { + 'label': _('Fee'), + 'items': ['Fees', 'Fee Schedule', 'Fee Structure'] + }, + { + 'label': _('Program'), + 'items': ['Program Enrollment'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan', 'Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/academic_year/academic_year.js b/erpnext/education/doctype/academic_year/academic_year.js index 21caa633699..0e8619849cd 100644 --- a/erpnext/education/doctype/academic_year/academic_year.js +++ b/erpnext/education/doctype/academic_year/academic_year.js @@ -1,10 +1,2 @@ -frappe.ui.form.on("Academic Year", "refresh", function(frm) { - if(!frm.doc.__islocal) { - frm.add_custom_button(__("Student Group"), function() { - frappe.route_options = { - academic_year: frm.doc.name - } - frappe.set_route("List", "Student Group"); - }); - } +frappe.ui.form.on("Academic Year", { }); \ No newline at end of file diff --git a/erpnext/education/doctype/academic_year/academic_year_dashboard.py b/erpnext/education/doctype/academic_year/academic_year_dashboard.py new file mode 100644 index 00000000000..f27f7d14cf6 --- /dev/null +++ b/erpnext/education/doctype/academic_year/academic_year_dashboard.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'academic_year', + 'transactions': [ + { + 'label': _('Student'), + 'items': ['Student Admission', 'Student Applicant', 'Student Group', 'Student Log'] + }, + { + 'label': _('Fee'), + 'items': ['Fees', 'Fee Schedule', 'Fee Structure'] + }, + { + 'label': _('Academic Term and Program'), + 'items': ['Academic Term', 'Program Enrollment'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan', 'Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js index 4c9c6f01f05..edfec26273b 100644 --- a/erpnext/education/doctype/article/article.js +++ b/erpnext/education/doctype/article/article.js @@ -3,6 +3,54 @@ frappe.ui.form.on('Article', { refresh: function(frm) { + if (!frm.doc.__islocal) { + frm.add_custom_button(__('Add to Topics'), function() { + frm.trigger('add_article_to_topics'); + }, __('Action')); + } + }, + add_article_to_topics: function(frm) { + get_topics_without_article(frm.doc.name).then(r => { + if (r.message.length) { + frappe.prompt([ + { + fieldname: 'topics', + label: __('Topics'), + fieldtype: 'MultiSelectPills', + get_data: function() { + return r.message; + } + } + ], + function(data) { + frappe.call({ + method: 'erpnext.education.doctype.topic.topic.add_content_to_topics', + args: { + 'content_type': 'Article', + 'content': frm.doc.name, + 'topics': data.topics, + }, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: __('...Adding Article to Topics') + }); + }, __('Add Article to Topics'), __('Add')); + } else { + frappe.msgprint(__('This article is already added to the existing topics')); + } + }); } }); + +let get_topics_without_article = function(article) { + return frappe.call({ + type: 'GET', + method: 'erpnext.education.doctype.article.article.get_topics_without_article', + args: {'article': article} + }); +}; \ No newline at end of file diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py index 7dc850be37c..8ba367da76e 100644 --- a/erpnext/education/doctype/article/article.py +++ b/erpnext/education/doctype/article/article.py @@ -7,9 +7,15 @@ import frappe from frappe.model.document import Document class Article(Document): - - def get_article(self): pass - +@frappe.whitelist() +def get_topics_without_article(article): + data = [] + for entry in frappe.db.get_all('Topic'): + topic = frappe.get_doc('Topic', entry.name) + topic_contents = [tc.content for tc in topic.topic_content] + if not topic_contents or article not in topic_contents: + data.append(topic.name) + return data \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py new file mode 100644 index 00000000000..2649d4b90c9 --- /dev/null +++ b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_group', + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Plan', 'Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.js b/erpnext/education/doctype/assessment_plan/assessment_plan.js index 0cb642bb6b2..c4c56143c3d 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan.js +++ b/erpnext/education/doctype/assessment_plan/assessment_plan.js @@ -2,9 +2,9 @@ // For license information, please see license.txt -frappe.ui.form.on("Assessment Plan", { +frappe.ui.form.on('Assessment Plan', { onload: function(frm) { - frm.set_query("assessment_group", function(doc, cdt, cdn) { + frm.set_query('assessment_group', function(doc, cdt, cdn) { return{ filters: { 'is_group': 0 @@ -22,20 +22,20 @@ frappe.ui.form.on("Assessment Plan", { refresh: function(frm) { if (frm.doc.docstatus == 1) { - frm.add_custom_button(__("Assessment Result"), function() { + frm.add_custom_button(__('Assessment Result Tool'), function() { frappe.route_options = { assessment_plan: frm.doc.name, student_group: frm.doc.student_group } - frappe.set_route("Form", "Assessment Result Tool"); - }); + frappe.set_route('Form', 'Assessment Result Tool'); + }, __('Tools')); } }, course: function(frm) { if (frm.doc.course && frm.doc.maximum_assessment_score) { frappe.call({ - method: "erpnext.education.api.get_assessment_criteria", + method: 'erpnext.education.api.get_assessment_criteria', args: { course: frm.doc.course }, @@ -43,12 +43,12 @@ frappe.ui.form.on("Assessment Plan", { if (r.message) { frm.doc.assessment_criteria = []; $.each(r.message, function(i, d) { - var row = frappe.model.add_child(frm.doc, "Assessment Plan Criteria", "assessment_criteria"); + var row = frappe.model.add_child(frm.doc, 'Assessment Plan Criteria', 'assessment_criteria'); row.assessment_criteria = d.assessment_criteria; row.maximum_score = d.weightage / 100 * frm.doc.maximum_assessment_score; }); } - refresh_field("assessment_criteria"); + refresh_field('assessment_criteria'); } }); @@ -56,6 +56,6 @@ frappe.ui.form.on("Assessment Plan", { }, maximum_assessment_score: function(frm) { - frm.trigger("course"); + frm.trigger('course'); } }); \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py index c36dfb11b54..5e6c29dcdf3 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -6,12 +6,16 @@ from frappe import _ def get_data(): return { 'fieldname': 'assessment_plan', - 'non_standard_fieldnames': { - }, 'transactions': [ { 'label': _('Assessment'), 'items': ['Assessment Result'] } + ], + 'reports': [ + { + 'label': _('Report'), + 'items': ['Assessment Plan Status'] + } ] } \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js index 84865ca8ecc..63d1aee0cb0 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.js +++ b/erpnext/education/doctype/assessment_result/assessment_result.js @@ -1,9 +1,16 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on("Assessment Result", { +frappe.ui.form.on('Assessment Result', { + refresh: function(frm) { + if (!frm.doc.__islocal) { + frm.trigger('setup_chart'); + } + frm.set_df_property('details', 'read_only', 1); + }, + onload: function(frm) { - frm.set_query('assessment_plan', function(){ + frm.set_query('assessment_plan', function() { return { filters: { docstatus: 1 @@ -15,48 +22,83 @@ frappe.ui.form.on("Assessment Result", { assessment_plan: function(frm) { if (frm.doc.assessment_plan) { frappe.call({ - method: "erpnext.education.api.get_assessment_details", + method: 'erpnext.education.api.get_assessment_details', args: { assessment_plan: frm.doc.assessment_plan }, callback: function(r) { if (r.message) { - frm.doc.details = []; + frappe.model.clear_table(frm.doc, 'details'); $.each(r.message, function(i, d) { - var row = frappe.model.add_child(frm.doc, "Assessment Result Detail", "details"); + var row = frm.add_child('details'); row.assessment_criteria = d.assessment_criteria; row.maximum_score = d.maximum_score; }); + frm.refresh_field('details'); } - refresh_field("details"); } }); } + }, + + setup_chart: function(frm) { + let labels = []; + let maximum_scores = []; + let scores = []; + $.each(frm.doc.details, function(_i, e) { + labels.push(e.assessment_criteria); + maximum_scores.push(e.maximum_score); + scores.push(e.score); + }); + + if (labels.length && maximum_scores.length && scores.length) { + frm.dashboard.chart_area.empty().removeClass('hidden'); + new frappe.Chart('.form-graph', { + title: 'Assessment Results', + data: { + labels: labels, + datasets: [ + { + name: 'Maximum Score', + chartType: 'bar', + values: maximum_scores, + }, + { + name: 'Score Obtained', + chartType: 'bar', + values: scores, + } + ] + }, + colors: ['#4CA746', '#98D85B'], + type: 'bar' + }); + } } }); -frappe.ui.form.on("Assessment Result Detail", { +frappe.ui.form.on('Assessment Result Detail', { score: function(frm, cdt, cdn) { var d = locals[cdt][cdn]; - if(!d.maximum_score || !frm.doc.grading_scale) { - d.score = ""; - frappe.throw(__("Please fill in all the details to generate Assessment Result.")); + if (!d.maximum_score || !frm.doc.grading_scale) { + d.score = ''; + frappe.throw(__('Please fill in all the details to generate Assessment Result.')); } if (d.score > d.maximum_score) { - frappe.throw(__("Score cannot be greater than Maximum Score")); + frappe.throw(__('Score cannot be greater than Maximum Score')); } else { frappe.call({ - method: "erpnext.education.api.get_grade", + method: 'erpnext.education.api.get_grade', args: { grading_scale: frm.doc.grading_scale, percentage: ((d.score/d.maximum_score) * 100) }, callback: function(r) { if (r.message) { - frappe.model.set_value(cdt, cdn, "grade", r.message); + frappe.model.set_value(cdt, cdn, 'grade', r.message); } } }); diff --git a/erpnext/education/doctype/assessment_result/assessment_result.json b/erpnext/education/doctype/assessment_result/assessment_result.json index 212d47cff09..7a893aabb8b 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.json +++ b/erpnext/education/doctype/assessment_result/assessment_result.json @@ -1,724 +1,182 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, - "allow_rename": 0, "autoname": "EDU-RES-.YYYY.-.#####", - "beta": 0, "creation": "2015-11-13 17:18:06.468332", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "assessment_plan", + "program", + "course", + "academic_year", + "academic_term", + "column_break_3", + "student", + "student_name", + "student_group", + "assessment_group", + "grading_scale", + "section_break_5", + "details", + "section_break_8", + "maximum_score", + "column_break_11", + "total_score", + "grade", + "section_break_13", + "comment", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_plan", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Assessment Plan", - "length": 0, - "no_copy": 0, "options": "Assessment Plan", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.program", "fieldname": "program", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Program" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.course", "fieldname": "course", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Course", - "length": 0, - "no_copy": 0, - "options": "Course", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Course" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.academic_year", "fieldname": "academic_year", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Academic Year" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.academic_term", "fieldname": "academic_term", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Academic Term" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "student", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Student", - "length": 0, - "no_copy": 0, "options": "Student", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student.title", "fieldname": "student_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, "label": "Student Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.student_group", "fieldname": "student_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Student Group", - "length": 0, - "no_copy": 0, - "options": "Student Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Student Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.assessment_group", "fieldname": "assessment_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Assessment Group", - "length": 0, - "no_copy": 0, - "options": "Assessment Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Assessment Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.grading_scale", "fieldname": "grading_scale", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Grading Scale", - "length": 0, - "no_copy": 0, "options": "Grading Scale", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_5", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Result" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fieldname": "details", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Details", - "length": 0, - "no_copy": 0, "options": "Assessment Result Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.maximum_assessment_score", "fieldname": "maximum_score", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Maximum Score", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assessment_plan.maximum_assessment_score", "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_score", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Total Score", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "grade", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Grade", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_13", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Summary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Summary" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "comment", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Comment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Comment" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Assessment Result", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-30 02:10:36.813413", + "links": [], + "modified": "2020-08-03 11:47:54.119486", "modified_by": "Administrator", "module": "Education", "name": "Assessment Result", - "name_case": "", "owner": "Administrator", "permissions": [ { @@ -728,28 +186,18 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Academics User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "student_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "student_name" } \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py new file mode 100644 index 00000000000..438379d08e4 --- /dev/null +++ b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'reports': [ + { + 'label': _('Reports'), + 'items': ['Final Assessment Grades', 'Course wise Assessment Report'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json index 85d943beaa5..450f41cbbb8 100644 --- a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json +++ b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json @@ -1,194 +1,66 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2016-12-14 17:44:35.583123", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-12-14 17:44:35.583123", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "assessment_criteria", + "maximum_score", + "column_break_2", + "score", + "grade" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "assessment_criteria", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Assessment Criteria", - "length": 0, - "no_copy": 0, - "options": "Assessment Criteria", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 4, + "fieldname": "assessment_criteria", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Assessment Criteria", + "options": "Assessment Criteria", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "maximum_score", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Score", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "maximum_score", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Maximum Score", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "score", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Score", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "score", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Score", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "grade", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Grade", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "columns": 2, + "fieldname": "grade", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Grade", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-11-10 19:11:14.362410", - "modified_by": "Administrator", - "module": "Education", - "name": "Assessment Result Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-07-31 13:27:17.699022", + "modified_by": "Administrator", + "module": "Education", + "name": "Assessment Result Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course.js b/erpnext/education/doctype/course/course.js index 69329896e0c..81e4a8c08d3 100644 --- a/erpnext/education/doctype/course/course.js +++ b/erpnext/education/doctype/course/course.js @@ -1,41 +1,60 @@ -frappe.ui.form.on("Course", "refresh", function(frm) { - if(!cur_frm.doc.__islocal) { - frm.add_custom_button(__("Program"), function() { - frappe.route_options = { - "Program Course.course": frm.doc.name - } - frappe.set_route("List", "Program"); - }); +frappe.ui.form.on('Course', { + refresh: function(frm) { + if (!cur_frm.doc.__islocal) { + frm.add_custom_button(__('Add to Programs'), function() { + frm.trigger('add_course_to_programs') + }, __('Action')); + } - frm.add_custom_button(__("Student Group"), function() { - frappe.route_options = { - course: frm.doc.name + frm.set_query('default_grading_scale', function(){ + return { + filters: { + docstatus: 1 + } } - frappe.set_route("List", "Student Group"); }); + }, - frm.add_custom_button(__("Course Schedule"), function() { - frappe.route_options = { - course: frm.doc.name + add_course_to_programs: function(frm) { + get_programs_without_course(frm.doc.name).then(r => { + if (r.message.length) { + frappe.prompt([ + { + fieldname: 'programs', + label: __('Programs'), + fieldtype: 'MultiSelectPills', + get_data: function() { + return r.message; + } + }, + { + fieldtype: 'Check', + label: __('Is Mandatory'), + fieldname: 'mandatory', + } + ], + function(data) { + frappe.call({ + method: 'erpnext.education.doctype.course.course.add_course_to_programs', + args: { + 'course': frm.doc.name, + 'programs': data.programs, + 'mandatory': data.mandatory + }, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: __('...Adding Course to Programs') + }) + }, __('Add Course to Programs'), __('Add')); + } else { + frappe.msgprint(__('This course is already added to the existing programs')); } - frappe.set_route("List", "Course Schedule"); - }); - - frm.add_custom_button(__("Assessment Plan"), function() { - frappe.route_options = { - course: frm.doc.name - } - frappe.set_route("List", "Assessment Plan"); }); } - - frm.set_query('default_grading_scale', function(){ - return { - filters: { - docstatus: 1 - } - } - }); }); frappe.ui.form.on('Course Topic', { @@ -50,3 +69,11 @@ frappe.ui.form.on('Course Topic', { }; } }); + +let get_programs_without_course = function(course) { + return frappe.call({ + type: 'GET', + method: 'erpnext.education.doctype.course.course.get_programs_without_course', + args: {'course': course} + }); +} \ No newline at end of file diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py index 0747a22f8da..06efa54e770 100644 --- a/erpnext/education/doctype/course/course.py +++ b/erpnext/education/doctype/course/course.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +import json from frappe.model.document import Document from frappe import _ @@ -17,12 +18,39 @@ class Course(Document): for criteria in self.assessment_criteria: total_weightage += criteria.weightage or 0 if total_weightage != 100: - frappe.throw(_("Total Weightage of all Assessment Criteria must be 100%")) + frappe.throw(_('Total Weightage of all Assessment Criteria must be 100%')) def get_topics(self): topic_data= [] for topic in self.topics: - topic_doc = frappe.get_doc("Topic", topic.topic) + topic_doc = frappe.get_doc('Topic', topic.topic) if topic_doc.topic_content: topic_data.append(topic_doc) - return topic_data \ No newline at end of file + return topic_data + + +@frappe.whitelist() +def add_course_to_programs(course, programs, mandatory=False): + programs = json.loads(programs) + for entry in programs: + program = frappe.get_doc('Program', entry) + program.append('courses', { + 'course': course, + 'course_name': course, + 'mandatory': mandatory + }) + program.flags.ignore_mandatory = True + program.save() + frappe.db.commit() + frappe.msgprint(_('Course {0} has been added to all the selected programs successfully.').format(frappe.bold(course)), + title=_('Programs updated'), indicator='green') + +@frappe.whitelist() +def get_programs_without_course(course): + data = [] + for entry in frappe.db.get_all('Program'): + program = frappe.get_doc('Program', entry.name) + courses = [c.course for c in program.courses] + if not courses or course not in courses: + data.append(program.name) + return data \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py index 752af29a9db..8a570bdc579 100644 --- a/erpnext/education/doctype/course/course_dashboard.py +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -6,12 +6,10 @@ from frappe import _ def get_data(): return { 'fieldname': 'course', - 'non_standard_fieldnames': { - }, 'transactions': [ { - 'label': _('Course'), - 'items': ['Course Enrollment', 'Course Schedule'] + 'label': _('Program and Course'), + 'items': ['Program', 'Course Enrollment', 'Course Schedule'] }, { 'label': _('Student'), @@ -19,7 +17,7 @@ def get_data(): }, { 'label': _('Assessment'), - 'items': ['Assessment Plan'] + 'items': ['Assessment Plan', 'Assessment Result'] }, ] } \ No newline at end of file diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py new file mode 100644 index 00000000000..b9dd457b61c --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'enrollment', + 'transactions': [ + { + 'label': _('Activity'), + 'items': ['Course Activity', 'Quiz Activity'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course_schedule/course_schedule.js b/erpnext/education/doctype/course_schedule/course_schedule.js index 692c2a83892..4275f6ef2a6 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.js +++ b/erpnext/education/doctype/course_schedule/course_schedule.js @@ -4,13 +4,13 @@ cur_frm.add_fetch("student_group", "course", "course") frappe.ui.form.on("Course Schedule", { refresh: function(frm) { if (!frm.doc.__islocal) { - frm.add_custom_button(__("Attendance"), function() { + frm.add_custom_button(__("Mark Attendance"), function() { frappe.route_options = { based_on: "Course Schedule", course_schedule: frm.doc.name } frappe.set_route("Form", "Student Attendance Tool"); - }); + }).addClass("btn-primary"); } } }); \ No newline at end of file diff --git a/erpnext/education/doctype/course_schedule/course_schedule.json b/erpnext/education/doctype/course_schedule/course_schedule.json index 7346cab438c..8c6746bda8c 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.json +++ b/erpnext/education/doctype/course_schedule/course_schedule.json @@ -1,520 +1,520 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2015-09-09 16:34:04.960369", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 0, + "creation": "2015-09-09 16:34:04.960369", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Student Group", - "length": 0, - "no_copy": 0, - "options": "Student Group", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_group", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Student Group", + "length": 0, + "no_copy": 0, + "options": "Student Group", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "instructor", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Instructor", - "length": 0, - "no_copy": 0, - "options": "Instructor", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "instructor", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Instructor", + "length": 0, + "no_copy": 0, + "options": "Instructor", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "instructor.Instructor_name", - "fieldname": "instructor_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Instructor Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "instructor.Instructor_name", + "fieldname": "instructor_name", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Instructor Name", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Naming Series", - "length": 0, - "no_copy": 0, - "options": "EDU-CSH-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Naming Series", + "length": 0, + "no_copy": 0, + "options": "EDU-CSH-.YYYY.-", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "course", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Course", - "length": 0, - "no_copy": 0, - "options": "Course", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "course", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "Course", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "color", - "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "color", + "fieldtype": "Color", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Color", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "schedule_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Schedule Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "schedule_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Schedule Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "room", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Room", - "length": 0, - "no_copy": 0, - "options": "Room", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "room", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Room", + "length": 0, + "no_copy": 0, + "options": "Room", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_9", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "from_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "From Time", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "to_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "To Time", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-08-21 14:44:51.827225", - "modified_by": "Administrator", - "module": "Education", - "name": "Course Schedule", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2018-08-21 14:44:51.827225", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Schedule", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "schedule_date", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0, + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Education", + "show_name_in_global_search": 0, + "sort_field": "schedule_date", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 0, + "track_seen": 0, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py new file mode 100644 index 00000000000..0866cd65357 --- /dev/null +++ b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course_schedule', + 'transactions': [ + { + 'label': _('Attendance'), + 'items': ['Student Attendance'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js index 13383312a8c..75dd4469e84 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js @@ -3,13 +3,13 @@ frappe.ui.form.on('Fee Schedule', { setup: function(frm) { - frm.add_fetch("fee_structure", "receivable_account", "receivable_account"); - frm.add_fetch("fee_structure", "income_account", "income_account"); - frm.add_fetch("fee_structure", "cost_center", "cost_center"); + frm.add_fetch('fee_structure', 'receivable_account', 'receivable_account'); + frm.add_fetch('fee_structure', 'income_account', 'income_account'); + frm.add_fetch('fee_structure', 'cost_center', 'cost_center'); }, onload: function(frm) { - frm.set_query("receivable_account", function(doc) { + frm.set_query('receivable_account', function(doc) { return { filters: { 'account_type': 'Receivable', @@ -18,7 +18,8 @@ frappe.ui.form.on('Fee Schedule', { } }; }); - frm.set_query("income_account", function(doc) { + + frm.set_query('income_account', function(doc) { return { filters: { 'account_type': 'Income Account', @@ -27,57 +28,59 @@ frappe.ui.form.on('Fee Schedule', { } }; }); - frm.set_query("student_group", "student_groups", function() { + + frm.set_query('student_group', 'student_groups', function() { return { - "program": frm.doc.program, - "academic_term": frm.doc.academic_term, - "academic_year": frm.doc.academic_year, - "disabled": 0 + 'program': frm.doc.program, + 'academic_term': frm.doc.academic_term, + 'academic_year': frm.doc.academic_year, + 'disabled': 0 }; }); - frappe.realtime.on("fee_schedule_progress", function(data) { + + frappe.realtime.on('fee_schedule_progress', function(data) { if (data.reload && data.reload === 1) { frm.reload_doc(); } if (data.progress) { - let progress_bar = $(cur_frm.dashboard.progress_area).find(".progress-bar"); + let progress_bar = $(cur_frm.dashboard.progress_area).find('.progress-bar'); if (progress_bar) { - $(progress_bar).removeClass("progress-bar-danger").addClass("progress-bar-success progress-bar-striped"); - $(progress_bar).css("width", data.progress+"%"); + $(progress_bar).removeClass('progress-bar-danger').addClass('progress-bar-success progress-bar-striped'); + $(progress_bar).css('width', data.progress+'%'); } } }); }, refresh: function(frm) { - if(!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info && - frm.doc.fee_creation_status=="Successful") { + if (!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info && + frm.doc.fee_creation_status === 'Successful') { var info = frm.doc.__onload.dashboard_info; frm.dashboard.add_indicator(__('Total Collected: {0}', [format_currency(info.total_paid, info.currency)]), 'blue'); frm.dashboard.add_indicator(__('Total Outstanding: {0}', [format_currency(info.total_unpaid, info.currency)]), info.total_unpaid ? 'orange' : 'green'); } - if (frm.doc.fee_creation_status=="In Process") { - frm.dashboard.add_progress("Fee Creation Status", "0"); + if (frm.doc.fee_creation_status === 'In Process') { + frm.dashboard.add_progress('Fee Creation Status', '0'); } - if (frm.doc.docstatus==1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status == "Failed") { + if (frm.doc.docstatus === 1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status === 'Failed') { frm.add_custom_button(__('Create Fees'), function() { frappe.call({ - method: "create_fees", + method: 'create_fees', doc: frm.doc, callback: function() { frm.refresh(); } }); - }, "fa fa-play", "btn-success"); + }).addClass('btn-primary');; } - if (frm.doc.fee_creation_status == "Successful") { - frm.add_custom_button(__("View Fees Records"), function() { + if (frm.doc.fee_creation_status === 'Successful') { + frm.add_custom_button(__('View Fees Records'), function() { frappe.route_options = { fee_schedule: frm.doc.name }; - frappe.set_route("List", "Fees"); + frappe.set_route('List', 'Fees'); }); } @@ -86,35 +89,35 @@ frappe.ui.form.on('Fee Schedule', { fee_structure: function(frm) { if (frm.doc.fee_structure) { frappe.call({ - method: "erpnext.education.doctype.fee_schedule.fee_schedule.get_fee_structure", + method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_fee_structure', args: { - "target_doc": frm.doc.name, - "source_name": frm.doc.fee_structure + 'target_doc': frm.doc.name, + 'source_name': frm.doc.fee_structure }, callback: function(r) { var doc = frappe.model.sync(r.message); - frappe.set_route("Form", doc[0].doctype, doc[0].name); + frappe.set_route('Form', doc[0].doctype, doc[0].name); } }); } } }); -frappe.ui.form.on("Fee Schedule Student Group", { +frappe.ui.form.on('Fee Schedule Student Group', { student_group: function(frm, cdt, cdn) { var row = locals[cdt][cdn]; if (row.student_group && frm.doc.academic_year) { frappe.call({ - method: "erpnext.education.doctype.fee_schedule.fee_schedule.get_total_students", + method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_total_students', args: { - "student_group": row.student_group, - "academic_year": frm.doc.academic_year, - "academic_term": frm.doc.academic_term, - "student_category": frm.doc.student_category + 'student_group': row.student_group, + 'academic_year': frm.doc.academic_year, + 'academic_term': frm.doc.academic_term, + 'student_category': frm.doc.student_category }, callback: function(r) { - if(!r.exc) { - frappe.model.set_value(cdt, cdn, "total_students", r.message); + if (!r.exc) { + frappe.model.set_value(cdt, cdn, 'total_students', r.message); } } }); diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json index 791831810ae..23b3212db22 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.json @@ -168,6 +168,7 @@ "fieldname": "grand_total_in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "read_only": 1 }, { @@ -272,7 +273,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-15 08:39:20.682837", + "modified": "2020-07-18 05:11:49.905457", "modified_by": "Administrator", "module": "Education", "name": "Fee Schedule", diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py new file mode 100644 index 00000000000..acfe4002193 --- /dev/null +++ b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py @@ -0,0 +1,13 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals + +def get_data(): + return { + 'fieldname': 'fee_schedule', + 'transactions': [ + { + 'items': ['Fees'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js index f09d2efcb9e..b331c6d3c0e 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.js +++ b/erpnext/education/doctype/fee_structure/fee_structure.js @@ -3,21 +3,21 @@ frappe.ui.form.on('Fee Structure', { setup: function(frm) { - frm.add_fetch("company", "default_receivable_account", "receivable_account"); - frm.add_fetch("company", "default_income_account", "income_account"); - frm.add_fetch("company", "cost_center", "cost_center"); + frm.add_fetch('company', 'default_receivable_account', 'receivable_account'); + frm.add_fetch('company', 'default_income_account', 'income_account'); + frm.add_fetch('company', 'cost_center', 'cost_center'); }, onload: function(frm) { - frm.set_query("academic_term", function() { + frm.set_query('academic_term', function() { return { - "filters": { - "academic_year": frm.doc.academic_year + 'filters': { + 'academic_year': frm.doc.academic_year } }; }); - frm.set_query("receivable_account", function(doc) { + frm.set_query('receivable_account', function(doc) { return { filters: { 'account_type': 'Receivable', @@ -26,7 +26,7 @@ frappe.ui.form.on('Fee Structure', { } }; }); - frm.set_query("income_account", function(doc) { + frm.set_query('income_account', function(doc) { return { filters: { 'account_type': 'Income Account', @@ -38,27 +38,27 @@ frappe.ui.form.on('Fee Structure', { }, refresh: function(frm) { - if(frm.doc.docstatus === 1) { + if (frm.doc.docstatus === 1) { frm.add_custom_button(__('Create Fee Schedule'), function() { frm.events.make_fee_schedule(frm); - }); + }).addClass('btn-primary'); } }, make_fee_schedule: function(frm) { frappe.model.open_mapped_doc({ - method: "erpnext.education.doctype.fee_structure.fee_structure.make_fee_schedule", + method: 'erpnext.education.doctype.fee_structure.fee_structure.make_fee_schedule', frm: frm }); } }); -frappe.ui.form.on("Fee Component", { +frappe.ui.form.on('Fee Component', { amount: function(frm) { var total_amount = 0; - for(var i=0;i| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.lab_test_uom -%}{{ row.lab_test_uom }}{%- endif -%}\n | \n\n\n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_particulars }} | \n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n | \n
| Antibiotic | \nSensitivity | \n
| {{ row.antibiotic }} | \n{{ row.antibiotic_sensitivity }} | \n
| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n | \n\n\n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_name }} | \n\t\t\t\t\n\t\t\t |
| {{ row.lab_test_particulars }} | \n\n |
| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n\t\t\t\t\t{%- if row.result_value -%}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n {{ row.result_value }}\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t{%- endif -%}\n \n\t\t\t\t\t{%- if row.secondary_uom and row.conversion_factor and row.secondary_uom_result -%}\n\t\t\t\t\t\t \n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n {{ row.secondary_uom_result }}\n {{ row.secondary_uom }}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t \n\t\t\t\t\t{%- endif -%}\n | \n\n \n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_name }} | \n\t\t\t\t\n\t\t\t |
| {{ row.lab_test_particulars }} | \n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n | \n
| Organism | \n\t\t\t\tColony Population | \n\t\t\t
| {{ row.organism }} | \n\t\t\t\t\n\t\t\t\t\t{{ row.colony_population }}\n\t\t\t\t\t{% if row.colony_uom %}\n\t\t\t\t\t\t{{ row.colony_uom }}\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t
| Antibiotic | \n\t\t\t\tSensitivity | \n\t\t\t
| {{ row.antibiotic }} | \n\t\t\t\t{{ row.antibiotic_sensitivity }} | \n\t\t\t
\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 0, + "line_breaks": 0, + "modified": "2020-04-29 16:47:02.743246", + "modified_by": "Administrator", + "module": "Selling", + "name": "GST POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/print_format/pos_invoice/__init__.py b/erpnext/selling/print_format/pos_invoice/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/print_format/pos_invoice/pos_invoice.json b/erpnext/selling/print_format/pos_invoice/pos_invoice.json new file mode 100644 index 00000000000..99094ed9b02 --- /dev/null +++ b/erpnext/selling/print_format/pos_invoice/pos_invoice.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2011-12-21 11:08:55", + "custom_format": 1, + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 1, + "line_breaks": 0, + "modified": "2020-04-29 16:45:58.942375", + "modified_by": "Administrator", + "module": "Selling", + "name": "POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/print_format/return_pos_invoice/__init__.py b/erpnext/selling/print_format/return_pos_invoice/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json b/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json new file mode 100644 index 00000000000..d7f335059ca --- /dev/null +++ b/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json @@ -0,0 +1,24 @@ +{ + "align_labels_right": 0, + "creation": "2020-05-14 17:02:44.207166", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Return Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Original Invoice\") }}: {{ doc.return_against }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc)}}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\")}}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 0, + "line_breaks": 0, + "modified": "2020-05-14 17:13:29.354015", + "modified_by": "Administrator", + "module": "Selling", + "name": "Return POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 7e8e6e9e8bd..f5feb95f1a8 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -96,7 +96,7 @@ def prepare_data(data, filters): # prepare data for report view row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) - row["delay"] = 0 if row["delay"] < 0 else row["delay"] + row["delay"] = 0 if row["delay"] and row["delay"] < 0 else row["delay"] if filters.get("group_by_so"): so_name = row["sales_order"] diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4a7dd5ad9b4..333a563aa5d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -142,7 +142,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); // check if child doctype is Sales Order Item/Qutation Item and calculate the rate - if(in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item"]), cdt) + if(in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item", "POS Invoice Item"]), cdt) this.apply_pricing_rule_on_item(item); else item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), @@ -312,6 +312,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ batch_no: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); + + if (item.serial_no) { + return; + } + item.serial_no = null; var has_serial_no; frappe.db.get_value('Item', {'item_code': item.item_code}, 'has_serial_no', (r) => { diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index ffc5daba62f..21fa4c3065f 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -1,31 +1,22 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - onload: function() { - if(cur_frm.doc.__onload && cur_frm.doc.__onload.quotation_series) { - cur_frm.fields_dict.quotation_series.df.options = cur_frm.doc.__onload.quotation_series; - cur_frm.refresh_field("quotation_series"); +frappe.ui.form.on("Shopping Cart Settings", { + onload: function(frm) { + if(frm.doc.__onload && frm.doc.__onload.quotation_series) { + frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series; + frm.refresh_field("quotation_series"); } }, - refresh: function(){ - toggle_mandatory(cur_frm) - }, - enable_checkout: function(){ - toggle_mandatory(cur_frm) - }, - enabled: function() { - if (cur_frm.doc.enabled === 1) { - cur_frm.doc.show_configure_button = 1; - cur_frm.refresh_field('show_configure_button'); + enabled: function(frm) { + if (frm.doc.enabled === 1) { + frm.set_value('enable_variants', 1); + } + else { + frm.set_value('company', ''); + frm.set_value('price_list', ''); + frm.set_value('default_customer_group', ''); + frm.set_value('quotation_series', ''); } } }); - - -function toggle_mandatory (cur_frm){ - cur_frm.toggle_reqd("payment_gateway_account", false); - if(cur_frm.doc.enabled && cur_frm.doc.enable_checkout) { - cur_frm.toggle_reqd("payment_gateway_account", true); - } -} diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index e828f54878b..32004efdca3 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -1,750 +1,193 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-19 15:57:32", - "custom": 0, - "description": "Default settings for Shopping Cart", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Enable Shopping Cart", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "display_settings", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Display Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "show_attachments", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Public Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "show_price", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Price", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_stock_availability", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Stock Availability", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_configure_button", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Configure Button", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "show_contact_us_button", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Contact Us Button", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "show_stock_availability", - "fieldname": "show_quantity_in_website", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Stock Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "show_apply_coupon_code_in_website", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Show Apply Coupon Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "allow_items_not_in_stock", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow items not in stock to be added to cart", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Prices will not be shown if Price List is not set", - "fieldname": "price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "default_customer_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quotation_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quotation Series", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:doc.enable_checkout", - "columns": 0, - "depends_on": "enabled", - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Checkout Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "fieldname": "enable_checkout", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Checkout", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Orders", - "description": "After payment completion redirect user to selected page.", - "fieldname": "payment_success_url", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Success Url", - "length": 0, - "no_copy": 0, - "options": "\nOrders\nInvoices\nMy Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_gateway_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-shopping-cart", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-10-14 13:54:24.575322", - "modified_by": "Administrator", - "module": "Shopping Cart", - "name": "Shopping Cart Settings", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 - } \ No newline at end of file + "actions": [], + "creation": "2013-06-19 15:57:32", + "description": "Default settings for Shopping Cart", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "enabled", + "display_settings", + "show_attachments", + "show_price", + "show_stock_availability", + "enable_variants", + "column_break_7", + "show_contact_us_button", + "show_quantity_in_website", + "show_apply_coupon_code_in_website", + "allow_items_not_in_stock", + "section_break_2", + "company", + "price_list", + "column_break_4", + "default_customer_group", + "quotation_series", + "section_break_8", + "enable_checkout", + "payment_success_url", + "column_break_11", + "payment_gateway_account" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enable Shopping Cart" + }, + { + "fieldname": "display_settings", + "fieldtype": "Section Break", + "label": "Display Settings" + }, + { + "default": "0", + "fieldname": "show_attachments", + "fieldtype": "Check", + "label": "Show Public Attachments" + }, + { + "default": "0", + "fieldname": "show_price", + "fieldtype": "Check", + "label": "Show Price" + }, + { + "default": "0", + "fieldname": "show_stock_availability", + "fieldtype": "Check", + "label": "Show Stock Availability" + }, + { + "default": "0", + "fieldname": "show_contact_us_button", + "fieldtype": "Check", + "label": "Show Contact Us Button" + }, + { + "default": "0", + "depends_on": "show_stock_availability", + "fieldname": "show_quantity_in_website", + "fieldtype": "Check", + "label": "Show Stock Quantity" + }, + { + "default": "0", + "fieldname": "show_apply_coupon_code_in_website", + "fieldtype": "Check", + "label": "Show Apply Coupon Code" + }, + { + "default": "0", + "fieldname": "allow_items_not_in_stock", + "fieldtype": "Check", + "label": "Allow items not in stock to be added to cart" + }, + { + "depends_on": "enabled", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "mandatory_depends_on": "eval: doc.enabled === 1", + "options": "Company", + "remember_last_selected_value": 1 + }, + { + "description": "Prices will not be shown if Price List is not set", + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "mandatory_depends_on": "eval: doc.enabled === 1", + "options": "Price List" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_customer_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Customer Group", + "mandatory_depends_on": "eval: doc.enabled === 1", + "options": "Customer Group" + }, + { + "fieldname": "quotation_series", + "fieldtype": "Select", + "label": "Quotation Series", + "mandatory_depends_on": "eval: doc.enabled === 1" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.enable_checkout", + "depends_on": "enabled", + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Checkout Settings" + }, + { + "default": "0", + "fieldname": "enable_checkout", + "fieldtype": "Check", + "label": "Enable Checkout" + }, + { + "default": "Orders", + "description": "After payment completion redirect user to selected page.", + "fieldname": "payment_success_url", + "fieldtype": "Select", + "label": "Payment Success Url", + "options": "\nOrders\nInvoices\nMy Account" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "payment_gateway_account", + "fieldtype": "Link", + "label": "Payment Gateway Account", + "options": "Payment Gateway Account" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "enable_variants", + "fieldtype": "Check", + "label": "Enable Variants" + } + ], + "icon": "fa fa-shopping-cart", + "idx": 1, + "issingle": 1, + "links": [], + "modified": "2020-08-02 18:21:43.873303", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Shopping Cart Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index 3098190383b..c069b90e986 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -80,6 +80,7 @@ def get_shopping_cart_settings(): return frappe.local.shopping_cart_settings +@frappe.whitelist(allow_guest=True) def is_cart_enabled(): return get_shopping_cart_settings().enabled diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index 90ecd462591..5545f13e8ca 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -50,11 +50,12 @@ def get_leaderboards(): return leaderboards @frappe.whitelist() -def get_all_customers(from_date, company, field, limit = None): +def get_all_customers(date_range, company, field, limit = None): if field == "outstanding_amount": filters = [['docstatus', '=', '1'], ['company', '=', company]] - if from_date: - filters.append(['posting_date', '>=', from_date]) + if date_range: + date_range = frappe.parse_json(date_range) + filters.append(['posting_date', '>=', 'between', [date_range[0], date_range[1]]]) return frappe.db.get_all('Sales Invoice', fields = ['customer as name', 'sum(outstanding_amount) as value'], filters = filters, @@ -68,18 +69,20 @@ def get_all_customers(from_date, company, field, limit = None): elif field == "total_qty_sold": select_field = "sum(so_item.stock_qty)" + date_condition = get_date_condition(date_range, 'so.transaction_date') + return frappe.db.sql(""" select so.customer as name, {0} as value FROM `tabSales Order` as so JOIN `tabSales Order Item` as so_item ON so.name = so_item.parent - where so.docstatus = 1 and so.transaction_date >= %s and so.company = %s + where so.docstatus = 1 {1} and so.company = %s group by so.customer order by value DESC limit %s - """.format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec + """.format(select_field, date_condition), (company, cint(limit)), as_dict=1) @frappe.whitelist() -def get_all_items(from_date, company, field, limit = None): +def get_all_items(date_range, company, field, limit = None): if field in ("available_stock_qty", "available_stock_value"): select_field = "sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)" return frappe.db.get_all('Bin', @@ -102,23 +105,25 @@ def get_all_items(from_date, company, field, limit = None): select_field = "sum(order_item.stock_qty)" select_doctype = "Purchase Order" + date_condition = get_date_condition(date_range, 'sales_order.transaction_date') + return frappe.db.sql(""" select order_item.item_code as name, {0} as value from `tab{1}` sales_order join `tab{1} Item` as order_item on sales_order.name = order_item.parent where sales_order.docstatus = 1 - and sales_order.company = %s and sales_order.transaction_date >= %s + and sales_order.company = %s {2} group by order_item.item_code order by value desc limit %s - """.format(select_field, select_doctype), (company, from_date, cint(limit)), as_dict=1) #nosec + """.format(select_field, select_doctype, date_condition), (company, cint(limit)), as_dict=1) #nosec @frappe.whitelist() -def get_all_suppliers(from_date, company, field, limit = None): +def get_all_suppliers(date_range, company, field, limit = None): if field == "outstanding_amount": filters = [['docstatus', '=', '1'], ['company', '=', company]] - if from_date: - filters.append(['posting_date', '>=', from_date]) + if date_range: + filters.append(['posting_date', 'between' [date_range[0], date_range[1]]]) return frappe.db.get_all('Purchase Invoice', fields = ['supplier as name', 'sum(outstanding_amount) as value'], filters = filters, @@ -132,18 +137,22 @@ def get_all_suppliers(from_date, company, field, limit = None): elif field == "total_qty_purchased": select_field = "sum(purchase_order_item.stock_qty)" + date_condition = get_date_condition(date_range, 'purchase_order.modified') + return frappe.db.sql(""" select purchase_order.supplier as name, {0} as value FROM `tabPurchase Order` as purchase_order LEFT JOIN `tabPurchase Order Item` as purchase_order_item ON purchase_order.name = purchase_order_item.parent - where purchase_order.docstatus = 1 and purchase_order.modified >= %s + where + purchase_order.docstatus = 1 + {1} and purchase_order.company = %s group by purchase_order.supplier order by value DESC - limit %s""".format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec + limit %s""".format(select_field, date_condition), (company, cint(limit)), as_dict=1) #nosec @frappe.whitelist() -def get_all_sales_partner(from_date, company, field, limit = None): +def get_all_sales_partner(date_range, company, field, limit = None): if field == "total_sales_amount": select_field = "sum(`base_net_total`)" elif field == "total_commission": @@ -154,8 +163,9 @@ def get_all_sales_partner(from_date, company, field, limit = None): 'docstatus': 1, 'company': company } - if from_date: - filters['transaction_date'] = ['>=', from_date] + if date_range: + date_range = frappe.parse_json(date_range) + filters['transaction_date'] = ['between', [date_range[0], date_range[1]]] return frappe.get_list('Sales Order', fields=[ '`sales_partner` as name', @@ -163,15 +173,27 @@ def get_all_sales_partner(from_date, company, field, limit = None): ], filters=filters, group_by='sales_partner', order_by='value DESC', limit=limit) @frappe.whitelist() -def get_all_sales_person(from_date, company, field = None, limit = 0): +def get_all_sales_person(date_range, company, field = None, limit = 0): + date_condition = get_date_condition(date_range, 'sales_order.transaction_date') + return frappe.db.sql(""" select sales_team.sales_person as name, sum(sales_order.base_net_total) as value from `tabSales Order` as sales_order join `tabSales Team` as sales_team on sales_order.name = sales_team.parent and sales_team.parenttype = 'Sales Order' where sales_order.docstatus = 1 - and sales_order.transaction_date >= %s and sales_order.company = %s + {date_condition} group by sales_team.sales_person order by value DESC limit %s - """, (from_date, company, cint(limit)), as_dict=1) + """.format(date_condition=date_condition), (company, cint(limit)), as_dict=1) + +def get_date_condition(date_range, field): + date_condition = '' + if date_range: + date_range = frappe.parse_json(date_range) + from_date, to_date = date_range + date_condition = "and {0} between {1} and {2}".format( + field, frappe.db.escape(from_date), frappe.db.escape(to_date) + ) + return date_condition \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json index 6da3b28baff..9c10a5346be 100644 --- a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json +++ b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json @@ -6,11 +6,11 @@ "docstatus": 0, "doctype": "Dashboard Chart", "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"to_date\":\"frappe.datetime.nowdate()\"}", - "filters_json": "{\"show_warehouse_wise_stock\":0}", + "filters_json": "{\"range1\":30,\"range2\":60,\"range3\":90,\"show_warehouse_wise_stock\":0}", "idx": 0, "is_public": 1, "is_standard": 1, - "modified": "2020-07-22 13:04:36.271198", + "modified": "2020-07-29 14:50:26.846482", "modified_by": "Administrator", "module": "Stock", "name": "Oldest Items", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 84d2057f960..66efcf8cd85 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -801,6 +801,7 @@ "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", + "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, @@ -851,6 +852,7 @@ "fieldname": "in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "oldfieldname": "in_words_export", "oldfieldtype": "Data", "print_hide": 1, @@ -1253,7 +1255,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-05-19 17:03:45.880106", + "modified": "2020-07-18 05:13:55.580420", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d5f479ff828..d7b43bf399b 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -13,7 +13,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError, from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for) from frappe import _, msgprint from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate, - now_datetime, random_string, strip, get_link_to_form) + now_datetime, random_string, strip, get_link_to_form, nowtime) from frappe.utils.html_utils import clean_html from frappe.website.doctype.website_slideshow.website_slideshow import \ get_slideshow @@ -194,7 +194,7 @@ class Item(WebsiteGenerator): if default_warehouse: stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse, qty=self.opening_stock, - rate=self.valuation_rate, company=default.company) + rate=self.valuation_rate, company=default.company, posting_date=getdate(), posting_time=nowtime()) stock_entry.add_comment("Comment", _("Opening Stock")) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 3a5ef769805..ee218f2f685 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -3,6 +3,9 @@ frappe.ui.form.on('Pick List', { setup: (frm) => { + frm.set_indicator_formatter('item_code', + function(doc) { return (doc.stock_qty === 0) ? "red" : "green"; }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery Note', 'Stock Entry': 'Stock Entry', diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 4b8b594ed9d..0da57b734b7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -26,11 +26,12 @@ class PickList(Document): continue if not item.serial_no: frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( - frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse))), + title=_("Serial Nos Required")) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') - .format(frappe.bold(item.item_code), frappe.bold(item.idx))) + .format(frappe.bold(item.item_code), frappe.bold(item.idx)), title=_("Quantity Mismatch")) def set_item_locations(self, save=False): items = self.aggregate_item_qty() @@ -40,6 +41,9 @@ class PickList(Document): if self.parent_warehouse: from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse) + # Create replica before resetting, to handle empty table on update after submit. + locations_replica = self.get('locations') + # reset self.delete_key('locations') for item_doc in items: @@ -48,7 +52,7 @@ class PickList(Document): self.item_location_map.setdefault(item_code, get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code), self.company)) - locations = get_items_with_location_and_quantity(item_doc, self.item_location_map) + locations = get_items_with_location_and_quantity(item_doc, self.item_location_map, self.docstatus) item_doc.idx = None item_doc.name = None @@ -62,6 +66,16 @@ class PickList(Document): location.update(row) self.append('locations', location) + # If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red + # and give feedback to the user. This is to avoid empty Pick Lists. + if not self.get('locations') and self.docstatus == 1: + for location in locations_replica: + location.stock_qty = 0 + location.picked_qty = 0 + self.append('locations', location) + frappe.msgprint(_("Please Restock Items and Update the Pick List to continue. To discontinue, cancel the Pick List."), + title=_("Out of Stock"), indicator="red") + if save: self.save() @@ -97,11 +111,13 @@ def validate_item_locations(pick_list): if not pick_list.locations: frappe.throw(_("Add items in the Item Locations table")) -def get_items_with_location_and_quantity(item_doc, item_location_map): +def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus): available_locations = item_location_map.get(item_doc.item_code) locations = [] - remaining_stock_qty = item_doc.stock_qty + # if stock qty is zero on submitted entry, show positive remaining qty to recalculate in case of restock. + remaining_stock_qty = item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty + while remaining_stock_qty > 0 and available_locations: item_location = available_locations.pop(0) item_location = frappe._dict(item_location) @@ -119,13 +135,11 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) - auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") - locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, + 'serial_no': serial_nos, 'batch_no': item_location.batch_no })) @@ -137,7 +151,7 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): item_location.qty = qty_diff if item_location.serial_no: # set remaining serial numbers - item_location.serial_no = item_location.serial_no[-qty_diff:] + item_location.serial_no = item_location.serial_no[-int(qty_diff):] available_locations = [item_location] + available_locations # update available locations for the item @@ -146,9 +160,14 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): def get_available_item_locations(item_code, from_warehouses, required_qty, company, ignore_validation=False): locations = [] - if frappe.get_cached_value('Item', item_code, 'has_serial_no'): + has_serial_no = frappe.get_cached_value('Item', item_code, 'has_serial_no') + has_batch_no = frappe.get_cached_value('Item', item_code, 'has_batch_no') + + if has_batch_no and has_serial_no: + locations = get_available_item_locations_for_serial_and_batched_item(item_code, from_warehouses, required_qty, company) + elif has_serial_no: locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company) - elif frappe.get_cached_value('Item', item_code, 'has_batch_no'): + elif has_batch_no: locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company) else: locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company) @@ -158,8 +177,9 @@ def get_available_item_locations(item_code, from_warehouses, required_qty, compa remaining_qty = required_qty - total_qty_available if remaining_qty > 0 and not ignore_validation: - frappe.msgprint(_('{0} units of {1} is not available.') - .format(remaining_qty, frappe.get_desk_link('Item', item_code))) + frappe.msgprint(_('{0} units of Item {1} is not available.') + .format(remaining_qty, frappe.get_desk_link('Item', item_code)), + title=_("Insufficient Stock")) return locations @@ -226,6 +246,34 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re return batch_locations +def get_available_item_locations_for_serial_and_batched_item(item_code, from_warehouses, required_qty, company): + # Get batch nos by FIFO + locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company) + + filters = frappe._dict({ + 'item_code': item_code, + 'company': company, + 'warehouse': ['!=', ''], + 'batch_no': '' + }) + + # Get Serial Nos by FIFO for Batch No + for location in locations: + filters.batch_no = location.batch_no + filters.warehouse = location.warehouse + location.qty = required_qty if location.qty > required_qty else location.qty # if extra qty in batch + + serial_nos = frappe.get_list('Serial No', + fields=['name'], + filters=filters, + limit=location.qty, + order_by='purchase_date') + + serial_nos = [sn.name for sn in serial_nos] + location.serial_no = serial_nos + + return locations + def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company): # gets all items available in different warehouses warehouses = [x.get('name') for x in frappe.get_list("Warehouse", {'company': company}, "name")] diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 1b9ff41cc33..8ea7f89dc4c 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -7,6 +7,8 @@ import frappe import unittest test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt +from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \ import EmptyStockReconciliationItemsError @@ -49,7 +51,7 @@ class TestPickList(unittest.TestCase): self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') self.assertEqual(pick_list.locations[0].qty, 5) - def test_pick_list_splits_row_according_to_warhouse_availability(self): + def test_pick_list_splits_row_according_to_warehouse_availability(self): try: frappe.get_doc({ 'doctype': 'Stock Reconciliation', @@ -122,7 +124,10 @@ class TestPickList(unittest.TestCase): }] }) - stock_reconciliation.submit() + try: + stock_reconciliation.submit() + except EmptyStockReconciliationItemsError: + pass pick_list = frappe.get_doc({ 'doctype': 'Pick List', @@ -145,6 +150,85 @@ class TestPickList(unittest.TestCase): self.assertEqual(pick_list.locations[0].qty, 5) self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') + def test_pick_list_shows_batch_no_for_batched_item(self): + # check if oldest batch no is picked + item = frappe.db.exists("Item", {'item_name': 'Batched Item'}) + if not item: + item = create_item("Batched Item") + item.has_batch_no = 1 + item.create_new_batch = 1 + item.batch_number_series = "B-BATCH-.##" + item.save() + else: + item = frappe.get_doc("Item", {'item_name': 'Batched Item'}) + + pr1 = make_purchase_receipt(item_code="Batched Item", qty=1, rate=100.0) + + pr1.load_from_db() + oldest_batch_no = pr1.items[0].batch_no + + pr2 = make_purchase_receipt(item_code="Batched Item", qty=2, rate=100.0) + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'purpose': 'Material Transfer', + 'locations': [{ + 'item_code': 'Batched Item', + 'qty': 1, + 'stock_qty': 1, + 'conversion_factor': 1, + }] + }) + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].batch_no, oldest_batch_no) + + pr1.cancel() + pr2.cancel() + + + def test_pick_list_for_batched_and_serialised_item(self): + # check if oldest batch no and serial nos are picked + item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'}) + if not item: + item = create_item("Batched and Serialised Item") + item.has_batch_no = 1 + item.create_new_batch = 1 + item.has_serial_no = 1 + item.batch_number_series = "B-BATCH-.##" + item.serial_no_series = "S-.####" + item.save() + else: + item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'}) + + pr1 = make_purchase_receipt(item_code="Batched and Serialised Item", qty=2, rate=100.0) + + pr1.load_from_db() + oldest_batch_no = pr1.items[0].batch_no + oldest_serial_nos = pr1.items[0].serial_no + + pr2 = make_purchase_receipt(item_code="Batched and Serialised Item", qty=2, rate=100.0) + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'purpose': 'Material Transfer', + 'locations': [{ + 'item_code': 'Batched and Serialised Item', + 'qty': 2, + 'stock_qty': 2, + 'conversion_factor': 1, + }] + }) + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].batch_no, oldest_batch_no) + self.assertEqual(pick_list.locations[0].serial_no, oldest_serial_nos) + + pr1.cancel() + pr2.cancel() + def test_pick_list_for_items_from_multiple_sales_orders(self): try: frappe.get_doc({ diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json index 71fbf9a866a..8665986004d 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -180,7 +180,7 @@ ], "istable": 1, "links": [], - "modified": "2020-03-13 19:08:21.995986", + "modified": "2020-06-24 17:18:57.357120", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index df9eb50843f..92e33ca64e3 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1,5 +1,7 @@ { + "actions": [], "allow_import": 1, + "allow_workflow": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", "doctype": "DocType", @@ -768,6 +770,7 @@ "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", + "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, @@ -820,6 +823,7 @@ "fieldname": "in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, @@ -1106,7 +1110,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-07-15 10:01:39.302238", + "modified": "2020-07-18 05:19:12.148115", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 153ce2fb674..f7ff916c5a2 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +import json from frappe.model.naming import make_autoname from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate @@ -537,15 +538,54 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note): return serial_nos @frappe.whitelist() -def auto_fetch_serial_number(qty, item_code, warehouse, batch_nos=None): - import json +def auto_fetch_serial_number(qty, item_code, warehouse, batch_nos=None, for_doctype=None): filters = { "item_code": item_code, "warehouse": warehouse, "delivery_document_no": "", "sales_invoice": "" } - if batch_nos: filters["batch_no"] = ["in", json.loads(batch_nos)] + + if batch_nos: + try: + filters["batch_no"] = ["in", json.loads(batch_nos)] + except: + filters["batch_no"] = ["in", [batch_nos]] + + if for_doctype == 'POS Invoice': + reserved_serial_nos, unreserved_serial_nos = get_pos_reserved_serial_nos(filters, qty) + return unreserved_serial_nos serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation") return [item['name'] for item in serial_numbers] + +@frappe.whitelist() +def get_pos_reserved_serial_nos(filters, qty=None): + batch_no_cond = "" + if filters.get("batch_no"): + batch_no_cond = "and item.batch_no = {}".format(frappe.db.escape(filters.get('batch_no'))) + + reserved_serial_nos_str = [d.serial_no for d in frappe.db.sql("""select item.serial_no as serial_no + from `tabPOS Invoice` p, `tabPOS Invoice Item` item + where p.name = item.parent + and p.consolidated_invoice is NULL + and p.docstatus = 1 + and item.docstatus = 1 + and item.item_code = %s + and item.warehouse = %s + {} + """.format(batch_no_cond), [filters.get('item_code'), filters.get('warehouse')], as_dict=1)] + + reserved_serial_nos = [] + for s in reserved_serial_nos_str: + if not s: continue + + serial_nos = s.split("\n") + serial_nos = ' '.join(serial_nos).split() # remove whitespaces + if len(serial_nos): reserved_serial_nos += serial_nos + + filters["name"] = ["not in", reserved_serial_nos] + serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation") + unreserved_serial_nos = [item['name'] for item in serial_numbers] + + return reserved_serial_nos, unreserved_serial_nos \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 0fbc63101e6..8e25804511e 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -413,7 +413,7 @@ class TestStockEntry(unittest.TestCase): def test_serial_item_error(self): se, serial_nos = self.test_serial_by_series() if not frappe.db.exists('Serial No', 'ABCD'): - make_serialized_item("_Test Serialized Item", "ABCD\nEFGH") + make_serialized_item(item_code="_Test Serialized Item", serial_no="ABCD\nEFGH") se = frappe.copy_doc(test_records[0]) se.purpose = "Material Transfer" @@ -823,15 +823,29 @@ class TestStockEntry(unittest.TestCase): ]) ) -def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): +def make_serialized_item(**args): + args = frappe._dict(args) se = frappe.copy_doc(test_records[0]) - se.get("items")[0].item_code = item_code or "_Test Serialized Item With Series" - se.get("items")[0].serial_no = serial_no + + if args.company: + se.company = args.company + + se.get("items")[0].item_code = args.item_code or "_Test Serialized Item With Series" + + if args.serial_no: + se.get("items")[0].serial_no = args.serial_no + + if args.cost_center: + se.get("items")[0].cost_center = args.cost_center + + if args.expense_account: + se.get("items")[0].expense_account = args.expense_account + se.get("items")[0].qty = 2 se.get("items")[0].transfer_qty = 2 - if target_warehouse: - se.get("items")[0].t_warehouse = target_warehouse + if args.target_warehouse: + se.get("items")[0].t_warehouse = args.target_warehouse se.set_stock_entry_type() se.insert() diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 3b49c4ca523..1cc600b9ca7 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -45,7 +45,6 @@ "oldfieldtype": "Section Break" }, { - "description": "If blank, parent Warehouse Account or company default will be considered", "fieldname": "warehouse_name", "fieldtype": "Data", "label": "Warehouse Name", @@ -86,6 +85,7 @@ "fieldtype": "Column Break" }, { + "description": "If blank, parent Warehouse Account or company default will be considered in transactions", "fieldname": "account", "fieldtype": "Link", "label": "Account", @@ -236,7 +236,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-07-16 15:43:50.653256", + "modified": "2020-08-03 18:41:52.442502", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b8554c83e24..1a7c15ebca7 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -401,13 +401,30 @@ def get_item_warehouse(item, args, overwrite_warehouse, defaults={}): return warehouse def update_barcode_value(out): - from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data barcode_data = get_barcode_data([out]) # If item has one barcode then update the value of the barcode field if barcode_data and len(barcode_data.get(out.item_code)) == 1: out['barcode'] = barcode_data.get(out.item_code)[0] +def get_barcode_data(items_list): + # get itemwise batch no data + # exmaple: {'LED-GRE': [Batch001, Batch002]} + # where LED-GRE is item code, SN0001 is serial no and Pune is warehouse + + itemwise_barcode = {} + for item in items_list: + barcodes = frappe.db.sql(""" + select barcode from `tabItem Barcode` where parent = %s + """, item.item_code, as_dict=1) + + for barcode in barcodes: + if item.item_code not in itemwise_barcode: + itemwise_barcode.setdefault(item.item_code, []) + itemwise_barcode[item.item_code].append(barcode.get("barcode")) + + return itemwise_barcode + @frappe.whitelist() def get_item_tax_info(company, tax_category, item_codes): out = {} diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js index ccde61a1679..8495142ba5b 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.js +++ b/erpnext/stock/report/stock_ageing/stock_ageing.js @@ -36,6 +36,27 @@ frappe.query_reports["Stock Ageing"] = { "fieldtype": "Link", "options": "Brand" }, + { + "fieldname":"range1", + "label": __("Ageing Range 1"), + "fieldtype": "Int", + "default": "30", + "reqd": 1 + }, + { + "fieldname":"range2", + "label": __("Ageing Range 2"), + "fieldtype": "Int", + "default": "60", + "reqd": 1 + }, + { + "fieldname":"range3", + "label": __("Ageing Range 3"), + "fieldtype": "Int", + "default": "90", + "reqd": 1 + }, { "fieldname":"show_warehouse_wise_stock", "label": __("Show Warehouse-wise Stock"), diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index d5878cb6624..4af3c541a69 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -4,12 +4,11 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import date_diff, flt +from frappe.utils import date_diff, flt, cint from six import iteritems from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos def execute(filters=None): - columns = get_columns(filters) item_details = get_fifo_queue(filters) to_date = filters["to_date"] @@ -25,6 +24,7 @@ def execute(filters=None): average_age = get_average_age(fifo_queue, to_date) earliest_age = date_diff(to_date, fifo_queue[0][1]) latest_age = date_diff(to_date, fifo_queue[-1][1]) + range1, range2, range3, above_range3 = get_range_age(filters, fifo_queue, to_date) row = [details.name, details.item_name, details.description, details.item_group, details.brand] @@ -33,6 +33,7 @@ def execute(filters=None): row.append(details.warehouse) row.extend([item_dict.get("total_qty"), average_age, + range1, range2, range3, above_range3, earliest_age, latest_age, details.stock_uom]) data.append(row) @@ -55,7 +56,25 @@ def get_average_age(fifo_queue, to_date): return flt(age_qty / total_qty, 2) if total_qty else 0.0 +def get_range_age(filters, fifo_queue, to_date): + range1 = range2 = range3 = above_range3 = 0.0 + for item in fifo_queue: + age = date_diff(to_date, item[1]) + + if age <= filters.range1: + range1 += flt(item[0]) + elif age <= filters.range2: + range2 += flt(item[0]) + elif age <= filters.range3: + range3 += flt(item[0]) + else: + above_range3 += flt(item[0]) + + return range1, range2, range3, above_range3 + def get_columns(filters): + range_columns = [] + setup_ageing_columns(filters, range_columns) columns = [ { "label": _("Item Code"), @@ -112,7 +131,9 @@ def get_columns(filters): "fieldname": "average_age", "fieldtype": "Float", "width": 100 - }, + }]) + columns.extend(range_columns) + columns.extend([ { "label": _("Earliest"), "fieldname": "earliest", @@ -263,3 +284,18 @@ def get_chart_data(data, filters): }, "type" : "bar" } + +def setup_ageing_columns(filters, range_columns): + for i, label in enumerate(["0-{range1}".format(range1=filters["range1"]), + "{range1}-{range2}".format(range1=cint(filters["range1"])+ 1, range2=filters["range2"]), + "{range2}-{range3}".format(range2=cint(filters["range2"])+ 1, range3=filters["range3"]), + "{range3}-{above}".format(range3=cint(filters["range3"])+ 1, above=_("Above"))]): + add_column(range_columns, label="Age ("+ label +")", fieldname='range' + str(i+1)) + +def add_column(range_columns, label, fieldname, fieldtype='Float', width=140): + range_columns.append(dict( + label=label, + fieldname=fieldname, + fieldtype=fieldtype, + width=width + )) \ No newline at end of file diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html index 04f89eca9db..b8b0d98bdc2 100644 --- a/erpnext/templates/generators/item/item_configure.html +++ b/erpnext/templates/generators/item/item_configure.html @@ -2,7 +2,7 @@ {% set cart_settings = shopping_cart.cart_settings %}