From 68b0d54b4b814dd3781fad07c1a2bd43b46ad59f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 9 Jan 2014 17:25:55 +0530 Subject: [PATCH 1/9] Set default accounts in company related to perpetual inventory, only it is enabled --- setup/doctype/company/company.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py index 12281b4415..88d9dcaaa5 100644 --- a/setup/doctype/company/company.py +++ b/setup/doctype/company/company.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import webnotes from webnotes import _, msgprint -from webnotes.utils import cstr +from webnotes.utils import cstr, cint import webnotes.defaults @@ -237,21 +237,28 @@ class DocType: account.insert() def set_default_accounts(self): - accounts = { + def _set_default_accounts(accounts): + for a in accounts: + account_name = accounts[a] + " - " + self.doc.abbr + if not self.doc.fields.get(a) and webnotes.conn.exists("Account", account_name): + webnotes.conn.set(self.doc, a, account_name) + + _set_default_accounts({ "default_income_account": "Sales", "default_expense_account": "Cost of Goods Sold", "receivables_group": "Accounts Receivable", "payables_group": "Accounts Payable", - "default_cash_account": "Cash", - "stock_received_but_not_billed": "Stock Received But Not Billed", - "stock_adjustment_account": "Stock Adjustment", - "expenses_included_in_valuation": "Expenses Included In Valuation" - } + "default_cash_account": "Cash" + }) + + if cint(webnotes.conn.get_value("Accounts Settings", None, "auto_accounting_for_stock")): + _set_default_accounts({ + "stock_received_but_not_billed": "Stock Received But Not Billed", + "stock_adjustment_account": "Stock Adjustment", + "expenses_included_in_valuation": "Expenses Included In Valuation" + }) + - for a in accounts: - account_name = accounts[a] + " - " + self.doc.abbr - if not self.doc.fields.get(a) and webnotes.conn.exists("Account", account_name): - webnotes.conn.set(self.doc, a, account_name) def create_default_cost_center(self): cc_list = [ From f3ded044e0686164f7271c130d8b2ad05bef7809 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 9 Jan 2014 17:38:50 +0530 Subject: [PATCH 2/9] Monthly Salary Register: Month is now optional --- hr/report/monthly_salary_register/monthly_salary_register.js | 2 +- hr/report/monthly_salary_register/monthly_salary_register.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hr/report/monthly_salary_register/monthly_salary_register.js b/hr/report/monthly_salary_register/monthly_salary_register.js index 5d3abccde9..32b4ef35fb 100644 --- a/hr/report/monthly_salary_register/monthly_salary_register.js +++ b/hr/report/monthly_salary_register/monthly_salary_register.js @@ -7,7 +7,7 @@ wn.query_reports["Monthly Salary Register"] = { "fieldname":"month", "label": wn._("Month"), "fieldtype": "Select", - "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", + "options": "\nJan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][wn.datetime.str_to_obj(wn.datetime.get_today()).getMonth()], }, diff --git a/hr/report/monthly_salary_register/monthly_salary_register.py b/hr/report/monthly_salary_register/monthly_salary_register.py index 8bd8f36ac8..9b8a84fd91 100644 --- a/hr/report/monthly_salary_register/monthly_salary_register.py +++ b/hr/report/monthly_salary_register/monthly_salary_register.py @@ -59,8 +59,8 @@ def get_columns(salary_slips): def get_salary_slips(filters): conditions, filters = get_conditions(filters) - salary_slips = webnotes.conn.sql("""select * from `tabSalary Slip` where docstatus = 1 %s""" % - conditions, filters, as_dict=1) + salary_slips = webnotes.conn.sql("""select * from `tabSalary Slip` where docstatus = 1 %s + order by employee, month""" % conditions, filters, as_dict=1) if not salary_slips: msgprint(_("No salary slip found for month: ") + cstr(filters.get("month")) + From 371663169c7d93f294d190c2c844a0293cf0543a Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Fri, 10 Jan 2014 15:05:44 +0530 Subject: [PATCH 3/9] pos view showing inclusive taxes --- accounts/doctype/sales_invoice/pos.js | 51 +++++++++++++++------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js index adbdca1b63..ce3784abd3 100644 --- a/accounts/doctype/sales_invoice/pos.js +++ b/accounts/doctype/sales_invoice/pos.js @@ -60,10 +60,16 @@ erpnext.POS = Class.extend({ \ \

\ - \ - \ +
\ +
\ + \ +
\ +
\ + \ +
\ +
\

\ \
\ @@ -82,7 +88,7 @@ erpnext.POS = Class.extend({ me.refresh(); }); - this.call_function("delete-items", function() {me.remove_selected_item();}); + this.call_function("remove-items", function() {me.remove_selected_items();}); this.call_function("make-payment", function() {me.make_payment();}); }, check_transaction_type: function() { @@ -333,7 +339,7 @@ erpnext.POS = Class.extend({ } this.disable_text_box_and_button(); - this.make_payment_button(); + this.hide_payment_button(); // If quotation to is not Customer then remove party if (this.frm.doctype == "Quotation") { @@ -370,21 +376,21 @@ erpnext.POS = Class.extend({ var taxes = wn.model.get_children(this.sales_or_purchase + " Taxes and Charges", this.frm.doc.name, this.frm.cscript.other_fname, this.frm.doctype); $(this.wrapper).find(".tax-table") - .toggle((taxes && taxes.length && - flt(me.frm.doc.other_charges_total_export || - me.frm.doc.other_charges_added_import) != 0.0) ? true : false) + .toggle((taxes && taxes.length) ? true : false) .find("tbody").empty(); $.each(taxes, function(i, d) { - $(repl('\ - %(description)s %(rate)s\ - %(tax_amount)s\ - ', { - description: d.description, - rate: ((d.charge_type == "Actual") ? '' : ("(" + d.rate + "%)")), - tax_amount: format_currency(flt(d.tax_amount)/flt(me.frm.doc.conversion_rate), - me.frm.doc.currency) - })).appendTo(".tax-table tbody"); + if (d.tax_amount) { + $(repl('\ + %(description)s %(rate)s\ + %(tax_amount)s\ + ', { + description: d.description, + rate: ((d.charge_type == "Actual") ? '' : ("(" + d.rate + "%)")), + tax_amount: format_currency(flt(d.tax_amount)/flt(me.frm.doc.conversion_rate), + me.frm.doc.currency) + })).appendTo(".tax-table tbody"); + } }); }, set_totals: function() { @@ -427,7 +433,7 @@ erpnext.POS = Class.extend({ $(this.wrapper).find('input, button').each(function () { $(this).prop('disabled', true); }); - $(this.wrapper).find(".delete-items").hide(); + $(this.wrapper).find(".remove-items").hide(); $(this.wrapper).find(".make-payment").hide(); } else { @@ -437,14 +443,14 @@ erpnext.POS = Class.extend({ $(this.wrapper).find(".make-payment").show(); } }, - make_payment_button: function() { + hide_payment_button: function() { var me = this; // Show Make Payment button only in Sales Invoice if (this.frm.doctype != "Sales Invoice") $(this.wrapper).find(".make-payment").hide(); }, refresh_delete_btn: function() { - $(this.wrapper).find(".delete-items").toggle($(".item-cart .warning").length ? true : false); + $(this.wrapper).find(".remove-items").toggle($(".item-cart .warning").length ? true : false); }, add_item_thru_barcode: function() { var me = this; @@ -466,7 +472,7 @@ erpnext.POS = Class.extend({ } }); }, - remove_selected_item: function() { + remove_selected_items: function() { var me = this; var selected_items = []; var no_of_items = $(this.wrapper).find("#cart tbody tr").length; @@ -487,6 +493,7 @@ erpnext.POS = Class.extend({ } } }); + this.refresh_grid(); }, refresh_grid: function() { From 03463ef73bf373af5a2415b093018c1e978f6a9a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 Jan 2014 16:28:41 +0530 Subject: [PATCH 4/9] Move related property setters to custom field property --- patches/1401/__init__.py | 0 ...elated_property_setters_to_custom_field.py | 35 +++++++++++++++++++ patches/patch_list.py | 1 + 3 files changed, 36 insertions(+) create mode 100644 patches/1401/__init__.py create mode 100644 patches/1401/p01_move_related_property_setters_to_custom_field.py diff --git a/patches/1401/__init__.py b/patches/1401/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patches/1401/p01_move_related_property_setters_to_custom_field.py b/patches/1401/p01_move_related_property_setters_to_custom_field.py new file mode 100644 index 0000000000..6b28553977 --- /dev/null +++ b/patches/1401/p01_move_related_property_setters_to_custom_field.py @@ -0,0 +1,35 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import webnotes +from webnotes.model.meta import get_field + +def execute(): + webnotes.reload_doc("core", "doctype", "custom_field") + + custom_fields = {} + for cf in webnotes.conn.sql("""select dt, fieldname from `tabCustom Field`""", as_dict=1): + custom_fields.setdefault(cf.dt, []).append(cf.fieldname) + + delete_list = [] + for ps in webnotes.conn.sql("""select * from `tabProperty Setter`""", as_dict=1): + if ps.field_name in custom_fields.get(ps.doc_type, []): + + if ps.property == "previous_field": + property_name = "insert_after" + + field_meta = get_field(ps.doc_type, ps.value) + property_value = field_meta.label if field_meta else "" + else: + property_name = ps.property + property_value =ps.value + + webnotes.conn.sql("""update `tabCustom Field` + set %s=%s where dt=%s and fieldname=%s""" % (property_name, '%s', '%s', '%s'), + (property_value, ps.doc_type, ps.field_name)) + + delete_list.append(ps.name) + + if delete_list: + webnotes.conn.sql("""delete from `tabProperty Setter` where name in (%s)""" % + ', '.join(['%s']*len(delete_list)), tuple(delete_list)) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 608ba77168..e4d2975ea3 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -263,4 +263,5 @@ patch_list = [ "patches.1311.p08_email_digest_recipients", "execute:webnotes.delete_doc('DocType', 'Warehouse Type')", "patches.1312.p02_update_item_details_in_item_price", + "patches.1401.p01_move_related_property_setters_to_custom_field", ] \ No newline at end of file From 35a9d585b4f767c0b8447dafb6dc2b96819707bb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 Jan 2014 12:24:27 +0530 Subject: [PATCH 5/9] Bank Reconciliation Statement: Show balance in debit or credit column based on account type --- .../bank_reconciliation_statement.js | 4 +- .../bank_reconciliation_statement.py | 53 ++++++++----------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js index b93f182f31..7f32e261c7 100644 --- a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -8,6 +8,7 @@ wn.query_reports["Bank Reconciliation Statement"] = { "label": wn._("Bank Account"), "fieldtype": "Link", "options": "Account", + "reqd": 1, "get_query": function() { return { "query": "accounts.utils.get_account_list", @@ -22,7 +23,8 @@ wn.query_reports["Bank Reconciliation Statement"] = { "fieldname":"report_date", "label": wn._("Date"), "fieldtype": "Date", - "default": get_today() + "default": get_today(), + "reqd": 1 }, ] } \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index 431a6496d2..5672497189 100644 --- a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -3,13 +3,14 @@ from __future__ import unicode_literals import webnotes -from webnotes import _, msgprint from webnotes.utils import flt def execute(filters=None): if not filters: filters = {} - - columns = get_columns() + + debit_or_credit = webnotes.conn.get_value("Account", filters["account"], "debit_or_credit") + + columns = get_columns() data = get_entries(filters) from accounts.utils import get_balance_on @@ -20,47 +21,39 @@ def execute(filters=None): total_debit += flt(d[4]) total_credit += flt(d[5]) - if webnotes.conn.get_value("Account", filters["account"], "debit_or_credit") == 'Debit': + if debit_or_credit == 'Debit': bank_bal = flt(balance_as_per_company) - flt(total_debit) + flt(total_credit) else: bank_bal = flt(balance_as_per_company) + flt(total_debit) - flt(total_credit) data += [ - ["", "", "", "Balance as per company books", balance_as_per_company, ""], + get_balance_row("Balance as per company books", balance_as_per_company, debit_or_credit), ["", "", "", "Amounts not reflected in bank", total_debit, total_credit], - ["", "", "", "Balance as per bank", bank_bal, ""] + get_balance_row("Balance as per bank", bank_bal, debit_or_credit) ] - - return columns, data + return columns, data def get_columns(): return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100", "Clearance Date:Date:110", "Against Account:Link/Account:200", "Debit:Currency:120", "Credit:Currency:120" ] - -def get_conditions(filters): - conditions = "" - if not filters.get("account"): - msgprint(_("Please select Bank Account"), raise_exception=1) - else: - conditions += " and jvd.account = %(account)s" - - if not filters.get("report_date"): - msgprint(_("Please select Date on which you want to run the report"), raise_exception=1) - else: - conditions += """ and jv.posting_date <= %(report_date)s - and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s""" - - return conditions def get_entries(filters): - conditions = get_conditions(filters) - entries = webnotes.conn.sql("""select jv.name, jv.posting_date, jv.clearance_date, - jvd.against_account, jvd.debit, jvd.credit - from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv - where jvd.parent = jv.name and jv.docstatus=1 and ifnull(jv.cheque_no, '')!= '' %s - order by jv.name DESC""" % conditions, filters, as_list=1) + entries = webnotes.conn.sql("""select + jv.name, jv.posting_date, jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit + from + `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 and ifnull(jv.cheque_no, '')!= '' + and jvd.account = %(account)s and jv.posting_date <= %(report_date)s + and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s + order by jv.name DESC""", filters, as_list=1) - return entries \ No newline at end of file + return entries + +def get_balance_row(label, amount, debit_or_credit): + if debit_or_credit == "Debit": + return ["", "", "", label, amount, 0] + else: + return ["", "", "", label, 0, amount] \ No newline at end of file From 14bf711d04e31cc5cb45ab0799ed6dab3a8ebcf5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 Jan 2014 13:28:07 +0530 Subject: [PATCH 6/9] Fixes in frozen accounts validation --- accounts/doctype/gl_entry/gl_entry.py | 3 ++- controllers/accounts_controller.py | 15 +++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index d3c6317787..694917ffcd 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -146,11 +146,12 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca webnotes.conn.sql("update `tab%s` set outstanding_amount=%s where name='%s'" % (against_voucher_type, bal, against_voucher)) -def validate_frozen_account(account, adv_adj): +def validate_frozen_account(account, adv_adj=None): frozen_account = webnotes.conn.get_value("Account", account, "freeze_account") if frozen_account == 'Yes' and not adv_adj: frozen_accounts_modifier = webnotes.conn.get_value( 'Accounts Settings', None, 'frozen_accounts_modifier') + if not frozen_accounts_modifier: webnotes.throw(account + _(" is a frozen account. \ Either make the account active or assign role in Accounts Settings \ diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index a65bf260ee..8d33327569 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import webnotes -from webnotes import _, msgprint, throw +from webnotes import _, throw from webnotes.utils import flt, cint, today, cstr from webnotes.model.code import get_obj from setup.utils import get_company_currency @@ -44,14 +44,13 @@ class AccountsController(TransactionBase): def validate_for_freezed_account(self): for fieldname in ["customer", "supplier"]: if self.meta.get_field(fieldname) and self.doc.fields.get(fieldname): - accounts = webnotes.conn.get_values("Account", {"master_type": fieldname.title(), - "master_name": self.doc.fields[fieldname], "company": self.doc.company}, - "freeze_account", as_dict=1) - + accounts = webnotes.conn.get_values("Account", + {"master_type": fieldname.title(), "master_name": self.doc.fields[fieldname], + "company": self.doc.company}, "name") if accounts: - if not filter(lambda x: cstr(x.freeze_account) in ["", "No"], accounts): - throw(_("Account for this ") + fieldname + _(" has been freezed. ") + - self.doc.doctype + _(" can not be made.")) + from accounts.doctype.gl_entry.gl_entry import validate_frozen_account + for account in accounts: + validate_frozen_account(account[0]) def set_price_list_currency(self, buying_or_selling): if self.meta.get_field("currency"): From 0b3c10601c6eedffcdfcbf2f91c4a238b2aa6ac9 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Mon, 13 Jan 2014 13:34:34 +0530 Subject: [PATCH 7/9] Increase and decrease quantity buttons in POS --- accounts/doctype/sales_invoice/pos.js | 39 ++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js index ce3784abd3..1d58f2d2dc 100644 --- a/accounts/doctype/sales_invoice/pos.js +++ b/accounts/doctype/sales_invoice/pos.js @@ -19,8 +19,10 @@ erpnext.POS = Class.extend({ \ \ \ - \ - \ + \ + \ + \ + \ \ \ \ @@ -357,8 +359,18 @@ erpnext.POS = Class.extend({ $(repl('\ \ - \ + \ + \ \ ', { @@ -370,6 +382,11 @@ erpnext.POS = Class.extend({ } )).appendTo($items); }); + + this.wrapper.find(".increase-qty, .decrease-qty").on("click", function() { + var item_code = $(this).closest("tr").attr("id"); + me.selected_item_qty_operation(item_code, $(this).attr("class")); + }); }, show_taxes: function() { var me = this; @@ -501,6 +518,22 @@ erpnext.POS = Class.extend({ this.frm.script_manager.trigger("calculate_taxes_and_totals"); this.refresh(); }, + selected_item_qty_operation: function(item_code, operation) { + var me = this; + var child = wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name, + this.frm.cscript.fname, this.frm.doctype); + + $.each(child, function(i, d) { + if (d.item_code == item_code) { + if (operation == "increase-qty") + d.qty += 1; + else if (operation == "decrease-qty") + d.qty != 1 ? d.qty -= 1 : d.qty = 1; + + me.refresh(); + } + }); + }, make_payment: function() { var me = this; var no_of_items = $(this.wrapper).find("#cart tbody tr").length; From c43d58ac79d6144eb6252ff611cc9605f290006d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 Jan 2014 17:55:24 +0530 Subject: [PATCH 8/9] Delete Property Setters for Custom Fields, and set them inside Custom Field --- ...elated_property_setters_to_custom_field.py | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/patches/1401/p01_move_related_property_setters_to_custom_field.py b/patches/1401/p01_move_related_property_setters_to_custom_field.py index 6b28553977..cf9221bcfd 100644 --- a/patches/1401/p01_move_related_property_setters_to_custom_field.py +++ b/patches/1401/p01_move_related_property_setters_to_custom_field.py @@ -2,34 +2,24 @@ # License: GNU General Public License v3. See license.txt import webnotes -from webnotes.model.meta import get_field def execute(): webnotes.reload_doc("core", "doctype", "custom_field") - custom_fields = {} - for cf in webnotes.conn.sql("""select dt, fieldname from `tabCustom Field`""", as_dict=1): - custom_fields.setdefault(cf.dt, []).append(cf.fieldname) - + cf_doclist = webnotes.get_doctype("Custom Field") + delete_list = [] - for ps in webnotes.conn.sql("""select * from `tabProperty Setter`""", as_dict=1): - if ps.field_name in custom_fields.get(ps.doc_type, []): - - if ps.property == "previous_field": - property_name = "insert_after" + for d in webnotes.conn.sql("""select cf.name as cf_name, ps.property, + ps.value, ps.name as ps_name + from `tabProperty Setter` ps, `tabCustom Field` cf + where ps.doctype_or_field = 'DocField' and ps.property != 'previous_field' + and ps.doc_type=cf.dt and ps.field_name=cf.fieldname""", as_dict=1): + if cf_doclist.get_field(d.property): + webnotes.conn.sql("""update `tabCustom Field` + set `%s`=%s where name=%s""" % (d.property, '%s', '%s'), (d.value, d.cf_name)) - field_meta = get_field(ps.doc_type, ps.value) - property_value = field_meta.label if field_meta else "" - else: - property_name = ps.property - property_value =ps.value + delete_list.append(d.ps_name) - webnotes.conn.sql("""update `tabCustom Field` - set %s=%s where dt=%s and fieldname=%s""" % (property_name, '%s', '%s', '%s'), - (property_value, ps.doc_type, ps.field_name)) - - delete_list.append(ps.name) - if delete_list: webnotes.conn.sql("""delete from `tabProperty Setter` where name in (%s)""" % ', '.join(['%s']*len(delete_list)), tuple(delete_list)) \ No newline at end of file From 1e347910d5a7e30dea13883fa5f6b651efa93146 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 13 Jan 2014 19:07:07 +0600 Subject: [PATCH 9/9] bumped to version 3.6.0 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 8499ee84d3..8e033d52a2 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.5.1", + "app_version": "3.6.0", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.5.0" + "requires_framework_version": "==3.7.0" } \ No newline at end of file
ItemQtyItemQtyRate
%(item_code)s%(item_name)s\ +
\ + \ +
\ +
\ +
\ + \ +
\ +
%(amount)s
%(rate)s