Merge remote-tracking branch 'upstream/develop' into sa-vat-report

This commit is contained in:
Anuja 2021-07-01 14:16:14 +05:30
commit 039034ec33
164 changed files with 9592 additions and 5283 deletions

View File

@ -151,6 +151,7 @@
"context": true,
"before": true,
"beforeEach": true,
"onScan": true
"onScan": true,
"extend_cscript": true
}
}

View File

@ -8,5 +8,8 @@
#
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# Replace use of Class.extend with native JS class
1fe891b287a1b3f225d29ee3d07e7b1824aba9e7
# This commit just changes spaces to tabs for indentation in some files
5f473611bd6ed57703716244a054d3fb5ba9cd23

View File

@ -44,3 +44,4 @@ sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
bench get-app erpnext "${GITHUB_WORKSPACE}"
bench start &
bench --site test_site reinstall --yes
bench build --app frappe

View File

@ -1,6 +1,10 @@
name: Server
on: [pull_request, workflow_dispatch]
on:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]
jobs:
test:
@ -87,6 +91,7 @@ jobs:
coveralls
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
COVERALLS_FLAG_NAME: run-${{ matrix.container }}
COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }}
COVERALLS_PARALLEL: true

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ latest_updates.json
.wnf-lang-status
*.egg-info
dist/
erpnext/public/dist
erpnext/docs/current
*.swp
*.swo

View File

@ -5,7 +5,7 @@
<p>ERP made simple</p>
</p>
[![CI](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml)
[![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '13.2.0'
__version__ = '13.6.0'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -15,7 +15,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
refresh: function (frm) {
frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
frappe.require("bank-reconciliation-tool.bundle.js", () =>
frm.trigger("make_reconciliation_tool")
);
frm.upload_statement_button = frm.page.set_secondary_action(

View File

@ -320,7 +320,7 @@ frappe.ui.form.on("Bank Statement Import", {
return;
}
frappe.require("/assets/js/data_import_tools.min.js", () => {
frappe.require("data_import_tools.bundle.js", () => {
frm.import_preview = new frappe.data_import.ImportPreview({
wrapper: frm.get_field("import_preview").$wrapper,
doctype: frm.doc.reference_doctype,

View File

@ -194,19 +194,19 @@ var update_jv_details = function(doc, r) {
refresh_field("accounts");
}
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() {
erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Controller {
onload() {
this.load_defaults();
this.setup_queries();
this.setup_balance_formatter();
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
},
}
onload_post_render: function() {
onload_post_render() {
cur_frm.get_field("accounts").grid.set_multiple_add("account");
},
}
load_defaults: function() {
load_defaults() {
//this.frm.show_print_first = true;
if(this.frm.doc.__islocal && this.frm.doc.company) {
frappe.model.set_default_values(this.frm.doc);
@ -216,9 +216,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
var posting_date = this.frm.doc.posting_date;
if(!this.frm.doc.amended_from) this.frm.set_value('posting_date', posting_date || frappe.datetime.get_today());
}
},
}
setup_queries: function() {
setup_queries() {
var me = this;
me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
@ -324,9 +324,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
});
},
}
setup_balance_formatter: function() {
setup_balance_formatter() {
const formatter = function(value, df, options, doc) {
var currency = frappe.meta.get_field_currency(df, doc);
var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
@ -337,9 +337,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
};
this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
},
}
reference_name: function(doc, cdt, cdn) {
reference_name(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
if(d.reference_name) {
@ -351,9 +351,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
}
}
},
}
get_outstanding: function(doctype, docname, company, child, due_date) {
get_outstanding(doctype, docname, company, child, due_date) {
var me = this;
var args = {
"doctype": doctype,
@ -375,9 +375,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
}
}
});
},
}
accounts_add: function(doc, cdt, cdn) {
accounts_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
$.each(doc.accounts, function(i, d) {
if(d.account && d.party && d.party_type) {
@ -400,9 +400,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
cur_frm.cscript.update_totals(doc);
erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts');
},
}
});
};
cur_frm.script_manager.make(erpnext.accounts.JournalEntry);

View File

@ -49,7 +49,15 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
doc: frm.doc,
btn: $(btn_primary),
method: "make_invoices",
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type])
freeze: 1,
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
callback: function(r) {
if (r.message.length == 1) {
frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type]));
} else if (r.message.length < 50) {
frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type]));
}
}
});
});

View File

@ -216,7 +216,8 @@ def start_import(invoices):
return names
def publish(index, total, doctype):
if total < 5: return
if total < 50:
return
frappe.publish_realtime(
"opening_invoice_creation_progress",
dict(
@ -241,4 +242,3 @@ def get_temporary_opening_account(company=None):
return accounts[0].name

View File

@ -7,6 +7,8 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null);

View File

@ -34,8 +34,8 @@ frappe.ui.form.on("Payment Reconciliation Payment", {
}
});
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
onload: function() {
erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller {
onload() {
var me = this;
this.frm.set_query("party", function() {
@ -84,18 +84,18 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
}
};
},
}
refresh: function() {
refresh() {
this.frm.disable_save();
this.toggle_primary_action();
},
}
onload_post_render: function() {
onload_post_render() {
this.toggle_primary_action();
},
}
party: function() {
party() {
var me = this
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
return frappe.call({
@ -112,9 +112,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
}
});
}
},
}
get_unreconciled_entries: function() {
get_unreconciled_entries() {
var me = this;
return this.frm.call({
doc: me.frm.doc,
@ -125,9 +125,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
}
});
},
}
reconcile: function() {
reconcile() {
var me = this;
var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
@ -209,9 +209,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
} else {
this.reconcile_payment_entries();
}
},
}
reconcile_payment_entries: function() {
reconcile_payment_entries() {
var me = this;
return this.frm.call({
@ -222,9 +222,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
me.toggle_primary_action();
}
});
},
}
set_invoice_options: function() {
set_invoice_options() {
var me = this;
var invoices = [];
@ -244,9 +244,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
}
refresh_field("payments");
},
}
toggle_primary_action: function() {
toggle_primary_action() {
if ((this.frm.doc.payments || []).length) {
this.frm.fields_dict.reconcile.$input
&& this.frm.fields_dict.reconcile.$input.addClass("btn-primary");
@ -260,6 +260,6 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
}
}
});
};
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));

View File

@ -142,7 +142,7 @@ class PeriodClosingVoucher(AccountsController):
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
from `tabGL Entry` t1, `tabAccount` t2
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
where t1.is_cancelled = 0 and t1.account = t2.name and t2.report_type = 'Profit and Loss'
and t2.docstatus < 2 and t2.company = %s
and t1.posting_date between %s and %s
group by t1.account, {dimension_fields}

View File

@ -4,18 +4,18 @@
{% include 'erpnext/selling/sales_common.js' %};
frappe.provide("erpnext.accounts");
erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnext.selling.SellingController {
setup(doc) {
this.setup_posting_date_time_check();
this._super(doc);
},
super.setup(doc);
}
company: function() {
company() {
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
},
}
onload(doc) {
this._super();
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
this.frm.script_manager.trigger("is_pos");
@ -23,10 +23,10 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
}
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
},
}
refresh(doc) {
this._super();
super.refresh();
if (doc.docstatus == 1 && !doc.is_return) {
this.frm.add_custom_button(__('Return'), this.make_sales_return, __('Create'));
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
@ -36,13 +36,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
this.frm.return_print_format = "Sales Invoice Return";
this.frm.set_value('consolidated_invoice', '');
}
},
}
is_pos: function() {
is_pos() {
this.set_pos_data();
},
}
set_pos_data: async function() {
async set_pos_data() {
if(this.frm.doc.is_pos) {
this.frm.set_value("allocate_advances_automatically", 0);
if(!this.frm.doc.company) {
@ -69,7 +69,7 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
}
}
}
},
}
customer() {
if (!this.frm.doc.customer) return
@ -86,13 +86,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
}, () => {
this.apply_pricing_rule();
});
},
}
amount: function(){
amount(){
this.write_off_outstanding_amount_automatically()
},
}
change_amount: function(){
change_amount(){
if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
this.calculate_write_off_amount();
}else {
@ -101,16 +101,16 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
}
this.frm.refresh_fields();
},
}
loyalty_amount: function(){
loyalty_amount(){
this.calculate_outstanding_amount();
this.frm.refresh_field("outstanding_amount");
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
},
}
write_off_outstanding_amount_automatically: function() {
write_off_outstanding_amount_automatically() {
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0
@ -125,17 +125,17 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
this.calculate_outstanding_amount(false);
this.frm.refresh_fields();
},
}
make_sales_return: function() {
make_sales_return() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return",
frm: cur_frm
})
},
})
}
}
$.extend(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
extend_cscript(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
frappe.ui.form.on('POS Invoice', {
redeem_loyalty_points: function(frm) {
@ -235,4 +235,4 @@ frappe.ui.form.on('POS Invoice', {
});
});
}
});
});

View File

@ -106,4 +106,4 @@
{{ terms_and_conditions }}
</div>
{% endif %}
</div>
</div>

View File

@ -286,7 +286,7 @@
}
],
"links": [],
"modified": "2021-05-21 10:14:22.426672",
"modified": "2021-05-21 11:14:22.426672",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@ -4,10 +4,10 @@
frappe.provide("erpnext.accounts");
{% include 'erpnext/public/js/controllers/buying.js' %};
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
setup: function(doc) {
erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.BuyingController {
setup(doc) {
this.setup_posting_date_time_check();
this._super(doc);
super.setup(doc);
// formatter for purchase invoice item
if(this.frm.doc.update_stock) {
@ -25,10 +25,10 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
};
});
},
}
onload: function() {
this._super();
onload() {
super.onload();
if(!this.frm.doc.__islocal) {
// show credit_to in print format
@ -44,11 +44,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
},
}
refresh: function(doc) {
refresh(doc) {
const me = this;
this._super();
super.refresh();
hide_fields(this.frm.doc);
// Show / Hide button
@ -157,26 +157,26 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
},
}
unblock_invoice: function() {
unblock_invoice() {
const me = this;
frappe.call({
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.unblock_invoice',
'args': {'name': me.frm.doc.name},
'callback': (r) => me.frm.reload_doc()
});
},
}
block_invoice: function() {
block_invoice() {
this.make_comment_dialog_and_block_invoice();
},
}
change_release_date: function() {
change_release_date() {
this.make_dialog_and_set_release_date();
},
}
can_change_release_date: function(date) {
can_change_release_date(date) {
const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate());
if (diff < 0) {
frappe.throw(__('New release date should be in the future'));
@ -184,9 +184,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
} else {
return true;
}
},
}
make_comment_dialog_and_block_invoice: function(){
make_comment_dialog_and_block_invoice(){
const me = this;
const title = __('Block Invoice');
@ -228,9 +228,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
});
this.dialog.show();
},
}
make_dialog_and_set_release_date: function() {
make_dialog_and_set_release_date() {
const me = this;
const title = __('Set New Release Date');
@ -259,17 +259,17 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
});
this.dialog.show();
},
}
set_release_date: function(data) {
set_release_date(data) {
return frappe.call({
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.change_release_date',
'args': data,
'callback': (r) => this.frm.reload_doc()
});
},
}
supplier: function() {
supplier() {
var me = this;
// Do not update if inter company reference is there as the details will already be updated
@ -291,9 +291,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
})
},
}
apply_tds: function(frm) {
apply_tds(frm) {
var me = this;
if (!me.frm.doc.apply_tds) {
@ -303,9 +303,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
}
},
}
credit_to: function() {
credit_to() {
var me = this;
if(this.frm.doc.credit_to) {
me.frm.call({
@ -323,16 +323,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
});
}
},
}
make_inter_company_invoice: function(frm) {
make_inter_company_invoice(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_inter_company_sales_invoice",
frm: frm
});
},
}
is_paid: function() {
is_paid() {
hide_fields(this.frm.doc);
if(cint(this.frm.doc.is_paid)) {
this.frm.set_value("allocate_advances_automatically", 0);
@ -343,44 +343,44 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
this.calculate_outstanding_amount();
this.frm.refresh_fields();
},
}
write_off_amount: function() {
write_off_amount() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.calculate_outstanding_amount();
this.frm.refresh_fields();
},
}
paid_amount: function() {
paid_amount() {
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
this.write_off_amount();
this.frm.refresh_fields();
},
}
allocated_amount: function() {
allocated_amount() {
this.calculate_total_advance();
this.frm.refresh_fields();
},
}
items_add: function(doc, cdt, cdn) {
items_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row,
["expense_account", "cost_center", "project"]);
},
}
on_submit: function() {
on_submit() {
$.each(this.frm.doc["items"] || [], function(i, row) {
if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt)
})
},
}
make_debit_note: function() {
make_debit_note() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
frm: cur_frm
})
},
});
}
};
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);

View File

@ -517,6 +517,8 @@ class PurchaseInvoice(BuyingController):
if d.category in ('Valuation', 'Total and Valuation')
and flt(d.base_tax_amount_after_discount_amount)]
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
@ -634,6 +636,34 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project
}, account_currency, item=item))
# check if the exchange rate has changed
if item.get('purchase_receipt'):
if exchange_rate_map[item.purchase_receipt] and \
self.conversion_rate != exchange_rate_map[item.purchase_receipt] and \
item.net_rate == net_rate_map[item.pr_detail]:
discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * \
(exchange_rate_map[item.purchase_receipt] - self.conversion_rate)
gl_entries.append(
self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
"debit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item)
)
gl_entries.append(
self.get_gl_dict({
"account": self.get_company_default("exchange_gain_loss_account"),
"against": self.supplier,
"credit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item)
)
# If asset is bought through this document and not linked to PR
if self.update_stock and item.landed_cost_voucher_amount:
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
@ -1141,6 +1171,36 @@ class PurchaseInvoice(BuyingController):
if update:
self.db_set('status', self.status, update_modified = update_modified)
# to get details of purchase invoice/receipt from which this doc was created for exchange rate difference handling
def get_purchase_document_details(doc):
if doc.doctype == 'Purchase Invoice':
doc_reference = 'purchase_receipt'
items_reference = 'pr_detail'
parent_doctype = 'Purchase Receipt'
child_doctype = 'Purchase Receipt Item'
else:
doc_reference = 'purchase_invoice'
items_reference = 'purchase_invoice_item'
parent_doctype = 'Purchase Invoice'
child_doctype = 'Purchase Invoice Item'
purchase_receipts_or_invoices = []
items = []
for item in doc.get('items'):
if item.get(doc_reference):
purchase_receipts_or_invoices.append(item.get(doc_reference))
if item.get(items_reference):
items.append(item.get(items_reference))
exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in',
purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1))
net_rate_map = frappe._dict(frappe.get_all(child_doctype, filters={'name': ('in',
items)}, fields=['name', 'net_rate'], as_list=1))
return exchange_rate_map, net_rate_map
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)

View File

@ -230,6 +230,27 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_purchase_invoice_with_exchange_rate_difference(self):
pr = make_purchase_receipt(currency = "USD", conversion_rate = 70)
pi = make_purchase_invoice(currency = "USD", conversion_rate = 80, do_not_save = "True")
pi.items[0].purchase_receipt = pr.name
pi.items[0].pr_detail = pr.items[0].name
pi.insert()
pi.submit()
# fetching the latest GL Entry with 'Exchange Gain/Loss - _TC' account
gl_entries = frappe.get_all('GL Entry', filters = {'account': 'Exchange Gain/Loss - _TC'})
voucher_no = frappe.get_value('GL Entry', gl_entries[0]['name'], 'voucher_no')
self.assertEqual(pi.name, voucher_no)
exchange_gain_loss_amount = frappe.get_value('GL Entry', gl_entries[0]['name'], 'debit')
discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
self.assertEqual(exchange_gain_loss_amount, discrepancy_caused_by_exchange_rate_diff)
def test_purchase_invoice_change_naming_series(self):
pi = frappe.copy_doc(test_records[1])
pi.insert()

View File

@ -854,7 +854,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2021-06-16 19:57:03.101571",
"modified": "2021-06-16 19:43:51.099386",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@ -5,17 +5,17 @@
frappe.provide("erpnext.accounts");
erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({
setup: function(doc) {
erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends erpnext.selling.SellingController {
setup(doc) {
this.setup_posting_date_time_check();
this._super(doc);
},
company: function() {
super.setup(doc);
}
company() {
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
},
onload: function() {
}
onload() {
var me = this;
this._super();
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', 'POS Closing Entry'];
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
@ -35,11 +35,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
erpnext.queries.setup_warehouse_query(this.frm);
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
},
}
refresh: function(doc, dt, dn) {
refresh(doc, dt, dn) {
const me = this;
this._super();
super.refresh();
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
// hide new msgbox
cur_frm.msgbox.hide();
@ -138,16 +138,16 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, __('Create'));
}
}
},
}
make_maintenance_schedule: function() {
make_maintenance_schedule() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
frm: cur_frm
})
},
}
on_submit: function(doc, dt, dn) {
on_submit(doc, dt, dn) {
var me = this;
if (frappe.get_route()[0] != 'Form') {
@ -157,9 +157,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
$.each(doc["items"], function(i, row) {
if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
})
},
}
set_default_print_format: function() {
set_default_print_format() {
// set default print format to POS type or Credit Note
if(cur_frm.doc.is_pos) {
if(cur_frm.pos_print_format) {
@ -180,9 +180,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
cur_frm.meta._default_print_format = null;
}
}
},
}
sales_order_btn: function() {
sales_order_btn() {
var me = this;
this.$sales_order_btn = this.frm.add_custom_button(__('Sales Order'),
function() {
@ -201,9 +201,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
})
}, __("Get Items From"));
},
}
quotation_btn: function() {
quotation_btn() {
var me = this;
this.$quotation_btn = this.frm.add_custom_button(__('Quotation'),
function() {
@ -225,9 +225,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
})
}, __("Get Items From"));
},
}
delivery_note_btn: function() {
delivery_note_btn() {
var me = this;
this.$delivery_note_btn = this.frm.add_custom_button(__('Delivery Note'),
function() {
@ -253,12 +253,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
});
}, __("Get Items From"));
},
}
tc_name: function() {
tc_name() {
this.get_terms();
},
customer: function() {
}
customer() {
if (this.frm.doc.is_pos){
var pos_profile = this.frm.doc.pos_profile;
}
@ -289,16 +289,16 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
});
}
},
}
make_inter_company_invoice: function() {
make_inter_company_invoice() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice",
frm: me.frm
});
},
}
debit_to: function() {
debit_to() {
var me = this;
if(this.frm.doc.debit_to) {
me.frm.call({
@ -316,14 +316,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
});
}
},
}
allocated_amount: function() {
allocated_amount() {
this.calculate_total_advance();
this.frm.refresh_fields();
},
}
write_off_outstanding_amount_automatically: function() {
write_off_outstanding_amount_automatically() {
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0
@ -338,39 +338,39 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.calculate_outstanding_amount(false);
this.frm.refresh_fields();
},
}
write_off_amount: function() {
write_off_amount() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.write_off_outstanding_amount_automatically();
},
}
items_add: function(doc, cdt, cdn) {
items_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "cost_center"]);
},
}
set_dynamic_labels: function() {
this._super();
set_dynamic_labels() {
super.set_dynamic_labels();
this.frm.events.hide_fields(this.frm)
},
}
items_on_form_rendered: function() {
items_on_form_rendered() {
erpnext.setup_serial_or_batch_no();
},
}
packed_items_on_form_rendered: function(doc, grid_row) {
packed_items_on_form_rendered(doc, grid_row) {
erpnext.setup_serial_or_batch_no();
},
}
make_sales_return: function() {
make_sales_return() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
frm: cur_frm
})
},
}
asset: function(frm, cdt, cdn) {
asset(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if(row.asset) {
frappe.call({
@ -384,18 +384,18 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
})
}
},
}
is_pos: function(frm){
is_pos(frm){
this.set_pos_data();
},
}
pos_profile: function() {
pos_profile() {
this.frm.doc.taxes = []
this.set_pos_data();
},
}
set_pos_data: function() {
set_pos_data() {
if(this.frm.doc.is_pos) {
this.frm.set_value("allocate_advances_automatically", 0);
if(!this.frm.doc.company) {
@ -425,13 +425,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
}
else this.frm.trigger("refresh");
},
}
amount: function(){
amount(){
this.write_off_outstanding_amount_automatically()
},
}
change_amount: function(){
change_amount(){
if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
this.calculate_write_off_amount();
}else {
@ -440,18 +440,18 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
this.frm.refresh_fields();
},
}
loyalty_amount: function(){
loyalty_amount(){
this.calculate_outstanding_amount();
this.frm.refresh_field("outstanding_amount");
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
}
});
};
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
cur_frm.cscript['Make Delivery Note'] = function() {
frappe.model.open_mapped_doc({

View File

@ -1957,6 +1957,33 @@ class TestSalesInvoice(unittest.TestCase):
einvoice = make_einvoice(si)
validate_totals(einvoice)
def test_item_tax_net_range(self):
item = create_item("T Shirt")
item.set('taxes', [])
item.append("taxes", {
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"minimum_net_rate": 0,
"maximum_net_rate": 500
})
item.append("taxes", {
"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
"minimum_net_rate": 501,
"maximum_net_rate": 1000
})
item.save()
sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
# Apply discount
sales_invoice.apply_discount_on = 'Net Total'
sales_invoice.discount_amount = 300
sales_invoice.save()
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = 'INV-2020-.#####'
@ -1985,33 +2012,6 @@ def get_sales_invoice_for_e_invoice():
return si
def test_item_tax_net_range(self):
item = create_item("T Shirt")
item.set('taxes', [])
item.append("taxes", {
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"minimum_net_rate": 0,
"maximum_net_rate": 500
})
item.append("taxes", {
"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
"minimum_net_rate": 501,
"maximum_net_rate": 1000
})
item.save()
sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
# Apply discount
sales_invoice.apply_discount_on = 'Net Total'
sales_invoice.discount_amount = 300
sales_invoice.save()
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
def make_test_address_for_ewaybill():
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
address = frappe.get_doc({

View File

@ -168,21 +168,24 @@ def get_columns(filters):
"label": _("Income"),
"fieldtype": "Currency",
"options": "currency",
"width": 120
"width": 305
},
{
"fieldname": "expense",
"label": _("Expense"),
"fieldtype": "Currency",
"options": "currency",
"width": 120
"width": 305
},
{
"fieldname": "gross_profit_loss",
"label": _("Gross Profit / Loss"),
"fieldtype": "Currency",
"options": "currency",
"width": 120
"width": 307
}
]

View File

@ -0,0 +1,451 @@
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// Contributed by Case Solved and sponsored by Nulight Studios
/* eslint-disable */
frappe.provide('frappe.query_reports');
frappe.query_reports["Tax Detail"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("company"),
reqd: 1
},
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.month_start(frappe.datetime.get_today()),
reqd: 1,
width: "60px"
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.month_end(frappe.datetime.get_today()),
reqd: 1,
width: "60px"
},
{
fieldname: "report_name",
label: __("Report Name"),
fieldtype: "Read Only",
default: frappe.query_report.report_name,
hidden: 1,
reqd: 1
},
{
fieldname: "mode",
label: __("Mode"),
fieldtype: "Read Only",
default: "edit",
hidden: 1,
reqd: 1
}
],
onload: function onload(report) {
// Remove Add Column and Save from menu
report.page.add_inner_button(__("New Report"), () => new_report(), __("Custom Report"));
report.page.add_inner_button(__("Load Report"), () => load_report(), __("Custom Report"));
hide_filters(report);
}
};
function hide_filters(report) {
report.page.page_form[0].querySelectorAll('.form-group.frappe-control').forEach(function setHidden(field) {
if (field.dataset.fieldtype == "Read Only") {
field.classList.add("hidden");
}
});
}
erpnext.TaxDetail = class TaxDetail {
constructor() {
this.patch();
this.load_report();
}
// Monkey patch the QueryReport class
patch() {
this.qr = frappe.query_report;
this.super = {
refresh_report: this.qr.refresh_report,
show_footer_message: this.qr.show_footer_message
}
this.qr.refresh_report = () => this.refresh_report();
this.qr.show_footer_message = () => this.show_footer_message();
}
show_footer_message() {
// The last thing to run after datatable_render in refresh()
this.super.show_footer_message.apply(this.qr);
if (this.qr.report_name !== 'Tax Detail') {
this.show_help();
if (this.loading) {
this.set_section('');
} else {
this.reload_component('');
}
}
this.loading = false;
}
refresh_report() {
// Infrequent report build (onload), load filters & data
// super function runs a refresh() serially
// already run within frappe.run_serially
this.loading = true;
this.super.refresh_report.apply(this.qr);
if (this.qr.report_name !== 'Tax Detail') {
frappe.call({
method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
args: {name: this.qr.report_name}
}).then((r) => {
const data = JSON.parse(r.message[this.qr.report_name]['json']);
this.create_controls();
this.sections = data.sections || {};
this.controls['show_detail'].set_input(data.show_detail);
});
}
}
load_report() {
// One-off report build like titles, menu, etc
// Run when this object is created which happens in qr.load_report
this.qr.menu_items = this.get_menu_items();
}
get_menu_items() {
// Replace Save action
let new_items = [];
const save = __('Save');
for (let item of this.qr.menu_items) {
if (item.label === save) {
new_items.push({
label: save,
action: () => this.save_report(),
standard: false
});
} else {
new_items.push(item);
}
}
return new_items;
}
save_report() {
this.check_datatable();
if (this.qr.report_name !== 'Tax Detail') {
frappe.call({
method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
args: {
reference_report: 'Tax Detail',
report_name: this.qr.report_name,
data: {
columns: this.qr.get_visible_columns(),
sections: this.sections,
show_detail: this.controls['show_detail'].get_input_value()
}
},
freeze: true
}).then((r) => {
this.set_section('');
});
}
}
check_datatable() {
if (!this.qr.datatable) {
frappe.throw(__('Please change the date range to load data first'));
}
}
set_section(name) {
// Sets the given section name and then reloads the data
if (name && !this.sections[name]) {
this.sections[name] = {};
}
let options = Object.keys(this.sections);
options.unshift('');
this.controls['section_name'].$wrapper.find("select").empty().add_options(options);
const org_mode = this.qr.get_filter_value('mode');
let refresh = false;
if (name) {
this.controls['section_name'].set_input(name);
this.qr.set_filter_value('mode', 'edit');
if (org_mode === 'run') {
refresh = true;
}
} else {
this.controls['section_name'].set_input('');
this.qr.set_filter_value('mode', 'run');
if (org_mode === 'edit') {
refresh = true;
}
}
if (refresh) {
this.qr.refresh();
}
this.reload_component('');
}
reload_component(component_name) {
const section_name = this.controls['section_name'].get_input_value();
if (section_name) {
const section = this.sections[section_name];
const component_names = Object.keys(section);
component_names.unshift('');
this.controls['component'].$wrapper.find("select").empty().add_options(component_names);
this.controls['component'].set_input(component_name);
if (component_name) {
this.controls['component_type'].set_input(section[component_name].type);
}
} else {
this.controls['component'].$wrapper.find("select").empty();
this.controls['component'].set_input('');
}
this.set_table_filters();
}
set_table_filters() {
let filters = {};
const section_name = this.controls['section_name'].get_input_value();
const component_name = this.controls['component'].get_input_value();
if (section_name && component_name) {
const component_type = this.sections[section_name][component_name].type;
if (component_type === 'filter') {
filters = this.sections[section_name][component_name]['filters'];
}
}
this.setAppliedFilters(filters);
}
setAppliedFilters(filters) {
if (this.qr.datatable) {
Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) {
let idx = input.dataset.colIndex;
if (filters[idx]) {
input.value = filters[idx];
} else {
input.value = null;
}
});
this.qr.datatable.columnmanager.applyFilter(filters);
}
}
delete(name, type) {
if (type === 'section') {
delete this.sections[name];
const new_section = Object.keys(this.sections)[0] || '';
this.set_section(new_section);
}
if (type === 'component') {
const cur_section = this.controls['section_name'].get_input_value();
delete this.sections[cur_section][name];
this.reload_component('');
}
}
create_controls() {
let controls = {};
// SELECT in data.js
controls['section_name'] = this.qr.page.add_field({
label: __('Section'),
fieldtype: 'Select',
fieldname: 'section_name',
change: (e) => {
this.set_section(this.controls['section_name'].get_input_value());
}
});
// BUTTON in button.js
controls['new_section'] = this.qr.page.add_field({
label: __('New Section'),
fieldtype: 'Button',
fieldname: 'new_section',
click: () => {
frappe.prompt({
label: __('Section Name'),
fieldname: 'name',
fieldtype: 'Data'
}, (values) => {
this.set_section(values.name);
});
}
});
controls['delete_section'] = this.qr.page.add_field({
label: __('Delete Section'),
fieldtype: 'Button',
fieldname: 'delete_section',
click: () => {
let cur_section = this.controls['section_name'].get_input_value();
if (cur_section) {
frappe.confirm(__('Are you sure you want to delete section') + ' ' + cur_section + '?',
() => {this.delete(cur_section, 'section')});
}
}
});
controls['component'] = this.qr.page.add_field({
label: __('Component'),
fieldtype: 'Select',
fieldname: 'component',
change: (e) => {
this.reload_component(this.controls['component'].get_input_value());
}
});
controls['component_type'] = this.qr.page.add_field({
label: __('Component Type'),
fieldtype: 'Select',
fieldname: 'component_type',
default: 'filter',
options: [
{label: __('Filtered Row Subtotal'), value: 'filter'},
{label: __('Section Subtotal'), value: 'section'}
]
});
controls['add_component'] = this.qr.page.add_field({
label: __('Add Component'),
fieldtype: 'Button',
fieldname: 'add_component',
click: () => {
this.check_datatable();
let section_name = this.controls['section_name'].get_input_value();
if (section_name) {
const component_type = this.controls['component_type'].get_input_value();
let idx = 0;
const names = Object.keys(this.sections[section_name]);
if (names.length > 0) {
const idxs = names.map((key) => parseInt(key.match(/\d+$/)) || 0);
idx = Math.max(...idxs) + 1;
}
const filters = this.qr.datatable.columnmanager.getAppliedFilters();
if (component_type === 'filter') {
const name = 'Filter' + idx.toString();
let data = {
type: component_type,
filters: filters
}
this.sections[section_name][name] = data;
this.reload_component(name);
} else if (component_type === 'section') {
if (filters && Object.keys(filters).length !== 0) {
frappe.show_alert({
message: __('Column filters ignored'),
indicator: 'yellow'
});
}
let data = {
type: component_type
}
frappe.prompt({
label: __('Section'),
fieldname: 'section',
fieldtype: 'Select',
options: Object.keys(this.sections)
}, (values) => {
this.sections[section_name][values.section] = data;
this.reload_component(values.section);
});
} else {
frappe.throw(__('Please select the Component Type first'));
}
} else {
frappe.throw(__('Please select the Section first'));
}
}
});
controls['delete_component'] = this.qr.page.add_field({
label: __('Delete Component'),
fieldtype: 'Button',
fieldname: 'delete_component',
click: () => {
const component = this.controls['component'].get_input_value();
if (component) {
frappe.confirm(__('Are you sure you want to delete component') + ' ' + component + '?',
() => {this.delete(component, 'component')});
}
}
});
controls['save'] = this.qr.page.add_field({
label: __('Save & Run'),
fieldtype: 'Button',
fieldname: 'save',
click: () => {
this.save_report();
}
});
controls['show_detail'] = this.qr.page.add_field({
label: __('Show Detail'),
fieldtype: 'Check',
fieldname: 'show_detail',
default: 1
});
this.controls = controls;
}
show_help() {
const help = __('Your custom report is built from General Ledger Entries within the date range. You can add multiple sections to the report using the New Section button. Each component added to a section adds a subset of the data into the specified section. Beware of duplicated data rows. The Filtered Row component type saves the datatable column filters to specify the added data. The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. The Amount column is summed to give the section subtotal. Use the Show Detail box to see the data rows included in each section in the final report. Once finished, hit Save & Run. Report contributed by');
this.qr.$report_footer.append('<div class="col-md-12"><strong>' + __('Help') + `: </strong>${help}<a href="https://www.casesolved.co.uk"> Case Solved</a></div>`);
}
}
if (!window.taxdetail) {
window.taxdetail = new erpnext.TaxDetail();
}
function get_reports(cb) {
frappe.call({
method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
freeze: true
}).then((r) => {
cb(r.message);
})
}
function new_report() {
const dialog = new frappe.ui.Dialog({
title: __('New Report'),
fields: [
{
fieldname: 'report_name',
label: __('Report Name'),
fieldtype: 'Data',
default: 'VAT Return'
}
],
primary_action_label: __('Create'),
primary_action: function new_report_pa(values) {
frappe.call({
method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
args: {
reference_report: 'Tax Detail',
report_name: values.report_name,
data: {
columns: [],
sections: {},
show_detail: 1
}
},
freeze: true
}).then((r) => {
frappe.set_route('query-report', values.report_name);
});
dialog.hide();
}
});
dialog.show();
}
function load_report() {
get_reports(function load_report_cb(reports) {
const dialog = new frappe.ui.Dialog({
title: __('Load Report'),
fields: [
{
fieldname: 'report_name',
label: __('Report Name'),
fieldtype: 'Select',
options: Object.keys(reports)
}
],
primary_action_label: __('Load'),
primary_action: function load_report_pa(values) {
dialog.hide();
frappe.set_route('query-report', values.report_name);
}
});
dialog.show();
});
}

View File

@ -0,0 +1,32 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2021-02-19 16:44:21.175113",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"modified": "2021-02-19 16:44:21.175113",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Detail",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "GL Entry",
"report_name": "Tax Detail",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
},
{
"role": "Auditor"
}
]
}

View File

@ -0,0 +1,296 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# Contributed by Case Solved and sponsored by Nulight Studios
from __future__ import unicode_literals
import frappe
import json
from frappe import _
# NOTE: Payroll is implemented using Journal Entries which are included as GL Entries
# field lists in multiple doctypes will be coalesced
required_sql_fields = {
("GL Entry", 1): ["posting_date"],
("Account",): ["root_type", "account_type"],
("GL Entry", 2): ["account", "voucher_type", "voucher_no", "debit", "credit"],
("Purchase Invoice Item", "Sales Invoice Item"): ["base_net_amount", "item_tax_rate", "item_tax_template", "item_group", "item_name"],
("Purchase Invoice", "Sales Invoice"): ["taxes_and_charges", "tax_category"],
}
def execute(filters=None):
if not filters:
return [], []
fieldlist = required_sql_fields
fieldstr = get_fieldstr(fieldlist)
gl_entries = frappe.db.sql("""
select {fieldstr}
from `tabGL Entry` ge
inner join `tabAccount` a on
ge.account=a.name and ge.company=a.company
left join `tabSales Invoice` si on
ge.company=si.company and ge.voucher_type='Sales Invoice' and ge.voucher_no=si.name
left join `tabSales Invoice Item` sii on
a.root_type='Income' and si.name=sii.parent
left join `tabPurchase Invoice` pi on
ge.company=pi.company and ge.voucher_type='Purchase Invoice' and ge.voucher_no=pi.name
left join `tabPurchase Invoice Item` pii on
a.root_type='Expense' and pi.name=pii.parent
where
ge.company=%(company)s and
ge.posting_date>=%(from_date)s and
ge.posting_date<=%(to_date)s
order by ge.posting_date, ge.voucher_no
""".format(fieldstr=fieldstr), filters, as_dict=1)
report_data = modify_report_data(gl_entries)
summary = None
if filters['mode'] == 'run' and filters['report_name'] != 'Tax Detail':
report_data, summary = run_report(filters['report_name'], report_data)
# return columns, data, message, chart, report_summary
return get_columns(fieldlist), report_data, None, None, summary
def run_report(report_name, data):
"Applies the sections and filters saved in the custom report"
report_config = json.loads(frappe.get_doc('Report', report_name).json)
# Columns indexed from 1 wrt colno
columns = report_config.get('columns')
sections = report_config.get('sections', {})
show_detail = report_config.get('show_detail', 1)
report = {}
new_data = []
summary = []
for section_name, section in sections.items():
report[section_name] = {'rows': [], 'subtotal': 0.0}
for component_name, component in section.items():
if component['type'] == 'filter':
for row in data:
matched = True
for colno, filter_string in component['filters'].items():
filter_field = columns[int(colno) - 1]['fieldname']
if not filter_match(row[filter_field], filter_string):
matched = False
break
if matched:
report[section_name]['rows'] += [row]
report[section_name]['subtotal'] += row['amount']
if component['type'] == 'section':
if component_name == section_name:
frappe.throw(_("A report component cannot refer to its parent section") + ": " + section_name)
try:
report[section_name]['rows'] += report[component_name]['rows']
report[section_name]['subtotal'] += report[component_name]['subtotal']
except KeyError:
frappe.throw(_("A report component can only refer to an earlier section") + ": " + section_name)
if show_detail:
new_data += report[section_name]['rows']
new_data += [{'voucher_no': section_name, 'amount': report[section_name]['subtotal']}]
summary += [{'label': section_name, 'datatype': 'Currency', 'value': report[section_name]['subtotal']}]
if show_detail:
new_data += [{}]
return new_data or data, summary or None
def filter_match(value, string):
"Approximation to datatable filters"
import datetime
if string == '':
return True
if value is None:
value = -999999999999999
elif isinstance(value, datetime.date):
return True
if isinstance(value, str):
value = value.lower()
string = string.lower()
if string[0] == '<':
return True if string[1:].strip() else False
elif string[0] == '>':
return False if string[1:].strip() else True
elif string[0] == '=':
return string[1:] in value if string[1:] else False
elif string[0:2] == '!=':
return string[2:] not in value
elif len(string.split(':')) == 2:
pre, post = string.split(':')
return (True if not pre.strip() and post.strip() in value else False)
else:
return string in value
else:
if string[0] in ['<', '>', '=']:
operator = string[0]
if operator == '=':
operator = '=='
string = string[1:].strip()
elif string[0:2] == '!=':
operator = '!='
string = string[2:].strip()
elif len(string.split(':')) == 2:
pre, post = string.split(':')
try:
return (True if float(pre) <= value and float(post) >= value else False)
except ValueError:
return (False if pre.strip() else True)
else:
return string in str(value)
try:
num = float(string) if string.strip() else 0
return frappe.safe_eval(f'{value} {operator} {num}')
except ValueError:
if operator == '<':
return True
return False
def abbrev(dt):
return ''.join(l[0].lower() for l in dt.split(' ')) + '.'
def doclist(dt, dfs):
return [abbrev(dt) + f for f in dfs]
def as_split(fields):
for field in fields:
split = field.split(' as ')
yield (split[0], split[1] if len(split) > 1 else split[0])
def coalesce(doctypes, fields):
coalesce = []
for name, new_name in as_split(fields):
sharedfields = ', '.join(abbrev(dt) + name for dt in doctypes)
coalesce += [f'coalesce({sharedfields}) as {new_name}']
return coalesce
def get_fieldstr(fieldlist):
fields = []
for doctypes, docfields in fieldlist.items():
if len(doctypes) == 1 or isinstance(doctypes[1], int):
fields += doclist(doctypes[0], docfields)
else:
fields += coalesce(doctypes, docfields)
return ', '.join(fields)
def get_columns(fieldlist):
columns = {}
for doctypes, docfields in fieldlist.items():
fieldmap = {name: new_name for name, new_name in as_split(docfields)}
for doctype in doctypes:
if isinstance(doctype, int):
break
meta = frappe.get_meta(doctype)
# get column field metadata from the db
fieldmeta = {}
for field in meta.get('fields'):
if field.fieldname in fieldmap.keys():
new_name = fieldmap[field.fieldname]
fieldmeta[new_name] = {
"label": _(field.label),
"fieldname": new_name,
"fieldtype": field.fieldtype,
"options": field.options
}
# edit the columns to match the modified data
for field in fieldmap.values():
col = modify_report_columns(doctype, field, fieldmeta[field])
if col:
columns[col["fieldname"]] = col
# use of a dict ensures duplicate columns are removed
return list(columns.values())
def modify_report_columns(doctype, field, column):
"Because data is rearranged into other columns"
if doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
if field in ["item_tax_rate", "base_net_amount"]:
return None
if doctype == "GL Entry" and field in ["debit", "credit"]:
column.update({"label": _("Amount"), "fieldname": "amount"})
if field == "taxes_and_charges":
column.update({"label": _("Taxes and Charges Template")})
return column
def modify_report_data(data):
import json
new_data = []
for line in data:
if line.debit:
line.amount = -line.debit
else:
line.amount = line.credit
# Remove Invoice GL Tax Entries and generate Tax entries from the invoice lines
if "Invoice" in line.voucher_type:
if line.account_type not in ("Tax", "Round Off"):
new_data += [line]
if line.item_tax_rate:
tax_rates = json.loads(line.item_tax_rate)
for account, rate in tax_rates.items():
tax_line = line.copy()
tax_line.account_type = "Tax"
tax_line.account = account
if line.voucher_type == "Sales Invoice":
line.amount = line.base_net_amount
tax_line.amount = line.base_net_amount * (rate / 100)
if line.voucher_type == "Purchase Invoice":
line.amount = -line.base_net_amount
tax_line.amount = -line.base_net_amount * (rate / 100)
new_data += [tax_line]
else:
new_data += [line]
return new_data
# JS client utilities
custom_report_dict = {
'ref_doctype': 'GL Entry',
'report_type': 'Custom Report',
'reference_report': 'Tax Detail'
}
@frappe.whitelist()
def get_custom_reports(name=None):
filters = custom_report_dict.copy()
if name:
filters['name'] = name
reports = frappe.get_list('Report',
filters = filters,
fields = ['name', 'json'],
as_list=False
)
reports_dict = {rep.pop('name'): rep for rep in reports}
# Prevent custom reports with the same name
reports_dict['Tax Detail'] = {'json': None}
return reports_dict
@frappe.whitelist()
def save_custom_report(reference_report, report_name, data):
if reference_report != 'Tax Detail':
frappe.throw(_("The wrong report is referenced."))
if report_name == 'Tax Detail':
frappe.throw(_("The parent report cannot be overwritten."))
doc = {
'doctype': 'Report',
'report_name': report_name,
'is_standard': 'No',
'module': 'Accounts',
'json': data
}
doc.update(custom_report_dict)
try:
newdoc = frappe.get_doc(doc)
newdoc.insert()
frappe.msgprint(_("Report created successfully"))
except frappe.exceptions.DuplicateEntryError:
dbdoc = frappe.get_doc('Report', report_name)
dbdoc.update(doc)
dbdoc.save()
frappe.msgprint(_("Report updated successfully"))
return report_name

View File

@ -0,0 +1,840 @@
[
{
"account_manager": null,
"accounts": [],
"companies": [],
"credit_limits": [],
"customer_details": null,
"customer_group": "All Customer Groups",
"customer_name": "_Test Customer",
"customer_pos_id": null,
"customer_primary_address": null,
"customer_primary_contact": null,
"customer_type": "Company",
"default_bank_account": null,
"default_commission_rate": 0.0,
"default_currency": null,
"default_price_list": null,
"default_sales_partner": null,
"disabled": 0,
"dn_required": 0,
"docstatus": 0,
"doctype": "Customer",
"email_id": null,
"gender": null,
"image": null,
"industry": null,
"is_frozen": 0,
"is_internal_customer": 0,
"language": "en",
"lead_name": null,
"loyalty_program": null,
"loyalty_program_tier": null,
"market_segment": null,
"mobile_no": null,
"modified": "2021-02-15 05:18:03.624724",
"name": "_Test Customer",
"naming_series": "CUST-.YYYY.-",
"pan": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"payment_terms": null,
"primary_address": null,
"represents_company": "",
"sales_team": [],
"salutation": null,
"so_required": 0,
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"territory": "All Territories",
"website": null
},{
"accounts": [],
"allow_purchase_invoice_creation_without_purchase_order": 0,
"allow_purchase_invoice_creation_without_purchase_receipt": 0,
"companies": [],
"country": "United Kingdom",
"default_bank_account": null,
"default_currency": null,
"default_price_list": null,
"disabled": 0,
"docstatus": 0,
"doctype": "Supplier",
"hold_type": "",
"image": null,
"is_frozen": 0,
"is_internal_supplier": 0,
"is_transporter": 0,
"language": "en",
"modified": "2021-03-31 16:47:10.109316",
"name": "_Test Supplier",
"naming_series": "SUP-.YYYY.-",
"on_hold": 0,
"pan": null,
"parent": null,
"parentfield": null,
"parenttype": null,
"payment_terms": null,
"prevent_pos": 0,
"prevent_rfqs": 0,
"release_date": null,
"represents_company": null,
"supplier_details": null,
"supplier_group": "Raw Material",
"supplier_name": "_Test Supplier",
"supplier_type": "Company",
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"warn_pos": 0,
"warn_rfqs": 0,
"website": null
},{
"account_currency": "GBP",
"account_name": "Debtors",
"account_number": "",
"account_type": "Receivable",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 58,
"modified": "2021-03-26 04:44:19.955468",
"name": "Debtors - _T",
"old_parent": null,
"parent": null,
"parent_account": "Application of Funds (Assets) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 59,
"root_type": "Asset",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Sales",
"account_number": "",
"account_type": "Income Account",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 291,
"modified": "2021-03-26 04:50:21.697703",
"name": "Sales - _T",
"old_parent": null,
"parent": null,
"parent_account": "Income - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Profit and Loss",
"rgt": 292,
"root_type": "Income",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "VAT on Sales",
"account_number": "",
"account_type": "Tax",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 317,
"modified": "2021-03-26 04:50:21.697703",
"name": "VAT on Sales - _T",
"old_parent": null,
"parent": null,
"parent_account": "Source of Funds (Liabilities) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 318,
"root_type": "Liability",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Cost of Goods Sold",
"account_number": "",
"account_type": "Cost of Goods Sold",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 171,
"modified": "2021-03-26 04:44:19.994857",
"name": "Cost of Goods Sold - _T",
"old_parent": null,
"parent": null,
"parent_account": "Expenses - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Profit and Loss",
"rgt": 172,
"root_type": "Expense",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "VAT on Purchases",
"account_number": "",
"account_type": "Tax",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 80,
"modified": "2021-03-26 04:44:19.961983",
"name": "VAT on Purchases - _T",
"old_parent": null,
"parent": null,
"parent_account": "Application of Funds (Assets) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 81,
"root_type": "Asset",
"tax_rate": 0.0
},{
"account_currency": "GBP",
"account_name": "Creditors",
"account_number": "",
"account_type": "Payable",
"balance_must_be": "",
"company": "_T",
"disabled": 0,
"docstatus": 0,
"doctype": "Account",
"freeze_account": "No",
"include_in_gross": 0,
"inter_company_account": 0,
"is_group": 0,
"lft": 302,
"modified": "2021-03-26 04:50:21.697703",
"name": "Creditors - _T",
"old_parent": null,
"parent": null,
"parent_account": "Source of Funds (Liabilities) - _T",
"parentfield": null,
"parenttype": null,
"report_type": "Balance Sheet",
"rgt": 303,
"root_type": "Liability",
"tax_rate": 0.0
},{
"additional_discount_percentage": 0.0,
"address_display": null,
"adjust_advance_taxes": 0,
"advances": [],
"against_expense_account": "Cost of Goods Sold - _T",
"allocate_advances_automatically": 0,
"amended_from": null,
"apply_discount_on": "Grand Total",
"apply_tds": 0,
"auto_repeat": null,
"base_discount_amount": 0.0,
"base_grand_total": 511.68,
"base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
"base_net_total": 426.4,
"base_paid_amount": 0.0,
"base_rounded_total": 511.68,
"base_rounding_adjustment": 0.0,
"base_taxes_and_charges_added": 85.28,
"base_taxes_and_charges_deducted": 0.0,
"base_total": 426.4,
"base_total_taxes_and_charges": 85.28,
"base_write_off_amount": 0.0,
"bill_date": null,
"bill_no": null,
"billing_address": null,
"billing_address_display": null,
"buying_price_list": "Standard Buying",
"cash_bank_account": null,
"clearance_date": null,
"company": "_T",
"contact_display": null,
"contact_email": null,
"contact_mobile": null,
"contact_person": null,
"conversion_rate": 1.0,
"cost_center": null,
"credit_to": "Creditors - _T",
"currency": "GBP",
"disable_rounded_total": 0,
"discount_amount": 0.0,
"docstatus": 0,
"doctype": "Purchase Invoice",
"due_date": null,
"from_date": null,
"grand_total": 511.68,
"group_same_items": 0,
"hold_comment": null,
"ignore_pricing_rule": 0,
"in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
"inter_company_invoice_reference": null,
"is_internal_supplier": 0,
"is_opening": "No",
"is_paid": 0,
"is_return": 0,
"is_subcontracted": "No",
"items": [
{
"allow_zero_valuation_rate": 0,
"amount": 426.4,
"asset_category": null,
"asset_location": null,
"base_amount": 426.4,
"base_net_amount": 426.4,
"base_net_rate": 5.33,
"base_price_list_rate": 5.33,
"base_rate": 5.33,
"base_rate_with_margin": 0.0,
"batch_no": null,
"bom": null,
"brand": null,
"conversion_factor": 0.0,
"cost_center": "Main - _T",
"deferred_expense_account": null,
"description": "<div class=\"ql-editor read-mode\"><p>Fluid to make widgets</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"enable_deferred_expense": 0,
"expense_account": "Cost of Goods Sold - _T",
"from_warehouse": null,
"image": null,
"include_exploded_items": 0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Widget Fluid 1Litre",
"item_tax_amount": 0.0,
"item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}",
"item_tax_template": null,
"landed_cost_voucher_amount": 0.0,
"manufacturer": null,
"manufacturer_part_no": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 426.4,
"net_rate": 5.33,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Purchase Invoice",
"po_detail": null,
"pr_detail": null,
"price_list_rate": 5.33,
"pricing_rules": null,
"project": null,
"purchase_invoice_item": null,
"purchase_order": null,
"purchase_receipt": null,
"qty": 80.0,
"quality_inspection": null,
"rate": 5.33,
"rate_with_margin": 0.0,
"received_qty": 0.0,
"rejected_qty": 0.0,
"rejected_serial_no": null,
"rejected_warehouse": null,
"rm_supp_cost": 0.0,
"sales_invoice_item": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"stock_qty": 0.0,
"stock_uom": "Nos",
"stock_uom_rate": 0.0,
"total_weight": 0.0,
"uom": "Nos",
"valuation_rate": 0.0,
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
}
],
"language": "en",
"letter_head": null,
"mode_of_payment": null,
"modified": "2021-04-03 03:33:09.180453",
"name": null,
"naming_series": "ACC-PINV-.YYYY.-",
"net_total": 426.4,
"on_hold": 0,
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Purchases</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Widget Fluid 1Litre</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
"outstanding_amount": 511.68,
"paid_amount": 0.0,
"parent": null,
"parentfield": null,
"parenttype": null,
"party_account_currency": "GBP",
"payment_schedule": [],
"payment_terms_template": null,
"plc_conversion_rate": 1.0,
"posting_date": null,
"posting_time": "16:59:56.789522",
"price_list_currency": "GBP",
"pricing_rules": [],
"project": null,
"rejected_warehouse": null,
"release_date": null,
"remarks": "No Remarks",
"represents_company": null,
"return_against": null,
"rounded_total": 511.68,
"rounding_adjustment": 0.0,
"scan_barcode": null,
"select_print_heading": null,
"set_from_warehouse": null,
"set_posting_time": 0,
"set_warehouse": null,
"shipping_address": null,
"shipping_address_display": "",
"shipping_rule": null,
"status": "Unpaid",
"supplied_items": [],
"supplier": "_Test Supplier",
"supplier_address": null,
"supplier_name": "_Test Supplier",
"supplier_warehouse": "Stores - _T",
"tax_category": null,
"tax_id": null,
"tax_withholding_category": null,
"taxes": [
{
"account_head": "VAT on Purchases - _T",
"add_deduct_tax": "Add",
"base_tax_amount": 85.28,
"base_tax_amount_after_discount_amount": 85.28,
"base_total": 511.68,
"category": "Total",
"charge_type": "On Net Total",
"cost_center": "Main - _T",
"description": "VAT on Purchases",
"included_in_print_rate": 0,
"item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}",
"parent": null,
"parentfield": "taxes",
"parenttype": "Purchase Invoice",
"rate": 0.0,
"row_id": null,
"tax_amount": 85.28,
"tax_amount_after_discount_amount": 85.28,
"total": 511.68
}
],
"taxes_and_charges": null,
"taxes_and_charges_added": 85.28,
"taxes_and_charges_deducted": 0.0,
"tc_name": null,
"terms": null,
"title": "_Purchase Invoice",
"to_date": null,
"total": 426.4,
"total_advance": 0.0,
"total_net_weight": 0.0,
"total_qty": 80.0,
"total_taxes_and_charges": 85.28,
"unrealized_profit_loss_account": null,
"update_stock": 0,
"write_off_account": null,
"write_off_amount": 0.0,
"write_off_cost_center": null
},{
"account_for_change_amount": null,
"additional_discount_percentage": 0.0,
"address_display": null,
"advances": [],
"against_income_account": "Sales - _T",
"allocate_advances_automatically": 0,
"amended_from": null,
"apply_discount_on": "Grand Total",
"auto_repeat": null,
"base_change_amount": 0.0,
"base_discount_amount": 0.0,
"base_grand_total": 868.25,
"base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
"base_net_total": 825.0,
"base_paid_amount": 0.0,
"base_rounded_total": 868.25,
"base_rounding_adjustment": 0.0,
"base_total": 825.0,
"base_total_taxes_and_charges": 43.25,
"base_write_off_amount": 0.0,
"c_form_applicable": "No",
"c_form_no": null,
"campaign": null,
"cash_bank_account": null,
"change_amount": 0.0,
"commission_rate": 0.0,
"company": "_T",
"company_address": null,
"company_address_display": null,
"company_tax_id": null,
"contact_display": null,
"contact_email": null,
"contact_mobile": null,
"contact_person": null,
"conversion_rate": 1.0,
"cost_center": null,
"currency": "GBP",
"customer": "_Test Customer",
"customer_address": null,
"customer_group": "All Customer Groups",
"customer_name": "_Test Customer",
"debit_to": "Debtors - _T",
"discount_amount": 0.0,
"docstatus": 0,
"doctype": "Sales Invoice",
"due_date": null,
"from_date": null,
"grand_total": 868.25,
"group_same_items": 0,
"ignore_pricing_rule": 0,
"in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
"inter_company_invoice_reference": null,
"is_consolidated": 0,
"is_discounted": 0,
"is_internal_customer": 0,
"is_opening": "No",
"is_pos": 0,
"is_return": 0,
"items": [
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 200.0,
"asset": null,
"barcode": null,
"base_amount": 200.0,
"base_net_amount": 200.0,
"base_net_rate": 50.0,
"base_price_list_rate": 0.0,
"base_rate": 50.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Dunlop tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 20.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 200.0,
"net_rate": 50.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 4.0,
"quality_inspection": null,
"rate": 50.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 4.0,
"stock_uom": "Nos",
"stock_uom_rate": 50.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
},
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 65.0,
"asset": null,
"barcode": null,
"base_amount": 65.0,
"base_net_amount": 65.0,
"base_net_rate": 65.0,
"base_price_list_rate": 0.0,
"base_rate": 65.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": "",
"item_group": null,
"item_name": "Continental tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 5.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 65.0,
"net_rate": 65.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 1.0,
"quality_inspection": null,
"rate": 65.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 1.0,
"stock_uom": null,
"stock_uom_rate": 65.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
},
{
"actual_batch_qty": 0.0,
"actual_qty": 0.0,
"allow_zero_valuation_rate": 0,
"amount": 560.0,
"asset": null,
"barcode": null,
"base_amount": 560.0,
"base_net_amount": 560.0,
"base_net_rate": 70.0,
"base_price_list_rate": 0.0,
"base_rate": 70.0,
"base_rate_with_margin": 0.0,
"batch_no": null,
"brand": null,
"conversion_factor": 1.0,
"cost_center": "Main - _T",
"customer_item_code": null,
"deferred_revenue_account": null,
"delivered_by_supplier": 0,
"delivered_qty": 0.0,
"delivery_note": null,
"description": "<div class=\"ql-editor read-mode\"><p>New</p></div>",
"discount_amount": 0.0,
"discount_percentage": 0.0,
"dn_detail": null,
"enable_deferred_revenue": 0,
"expense_account": null,
"finance_book": null,
"image": null,
"income_account": "Sales - _T",
"incoming_rate": 0.0,
"is_fixed_asset": 0,
"is_free_item": 0,
"item_code": null,
"item_group": null,
"item_name": "Toyo tyres",
"item_tax_rate": "{\"VAT on Sales - _T\": 0.0}",
"item_tax_template": null,
"margin_rate_or_amount": 0.0,
"margin_type": "",
"net_amount": 560.0,
"net_rate": 70.0,
"page_break": 0,
"parent": null,
"parentfield": "items",
"parenttype": "Sales Invoice",
"price_list_rate": 0.0,
"pricing_rules": null,
"project": null,
"qty": 8.0,
"quality_inspection": null,
"rate": 70.0,
"rate_with_margin": 0.0,
"sales_invoice_item": null,
"sales_order": null,
"serial_no": null,
"service_end_date": null,
"service_start_date": null,
"service_stop_date": null,
"so_detail": null,
"stock_qty": 8.0,
"stock_uom": null,
"stock_uom_rate": 70.0,
"target_warehouse": null,
"total_weight": 0.0,
"uom": "Nos",
"warehouse": null,
"weight_per_unit": 0.0,
"weight_uom": null
}
],
"language": "en",
"letter_head": null,
"loyalty_amount": 0.0,
"loyalty_points": 0,
"loyalty_program": null,
"loyalty_redemption_account": null,
"loyalty_redemption_cost_center": null,
"modified": "2021-02-16 05:18:59.755144",
"name": null,
"naming_series": "ACC-SINV-.YYYY.-",
"net_total": 825.0,
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Sales</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Dunlop tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Continental tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Toyo tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
"outstanding_amount": 868.25,
"packed_items": [],
"paid_amount": 0.0,
"parent": null,
"parentfield": null,
"parenttype": null,
"party_account_currency": "GBP",
"payment_schedule": [],
"payment_terms_template": null,
"payments": [],
"plc_conversion_rate": 1.0,
"po_date": null,
"po_no": "",
"pos_profile": null,
"posting_date": null,
"posting_time": "5:19:02.994077",
"price_list_currency": "GBP",
"pricing_rules": [],
"project": null,
"redeem_loyalty_points": 0,
"remarks": "No Remarks",
"represents_company": "",
"return_against": null,
"rounded_total": 868.25,
"rounding_adjustment": 0.0,
"sales_partner": null,
"sales_team": [],
"scan_barcode": null,
"select_print_heading": null,
"selling_price_list": "Standard Selling",
"set_posting_time": 0,
"set_target_warehouse": null,
"set_warehouse": null,
"shipping_address": null,
"shipping_address_name": "",
"shipping_rule": null,
"source": null,
"status": "Overdue",
"tax_category": "",
"tax_id": null,
"taxes": [
{
"account_head": "VAT on Sales - _T",
"base_tax_amount": 43.25,
"base_tax_amount_after_discount_amount": 43.25,
"base_total": 868.25,
"charge_type": "On Net Total",
"cost_center": "Main - _T",
"description": "VAT on Sales",
"included_in_print_rate": 0,
"item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}",
"parent": null,
"parentfield": "taxes",
"parenttype": "Sales Invoice",
"rate": 0.0,
"row_id": null,
"tax_amount": 43.25,
"tax_amount_after_discount_amount": 43.25,
"total": 868.25
}
],
"taxes_and_charges": null,
"tc_name": null,
"terms": null,
"territory": "All Territories",
"timesheets": [],
"title": "_Sales Invoice",
"to_date": null,
"total": 825.0,
"total_advance": 0.0,
"total_billing_amount": 0.0,
"total_commission": 0.0,
"total_net_weight": 0.0,
"total_qty": 13.0,
"total_taxes_and_charges": 43.25,
"unrealized_profit_loss_account": null,
"update_billed_amount_in_sales_order": 0,
"update_stock": 0,
"write_off_account": null,
"write_off_amount": 0.0,
"write_off_cost_center": null,
"write_off_outstanding_amount_automatically": 0
}
]

View File

@ -0,0 +1,176 @@
from __future__ import unicode_literals
import frappe
import unittest
import datetime
import json
import os
from frappe.utils import getdate, add_to_date, get_first_day, get_last_day, get_year_start, get_year_ending
from .tax_detail import filter_match, save_custom_report
class TestTaxDetail(unittest.TestCase):
def load_testdocs(self):
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
datapath, _ = os.path.splitext(os.path.realpath(__file__))
with open(datapath + '.json', 'r') as fp:
docs = json.load(fp)
now = getdate()
self.from_date = get_first_day(now)
self.to_date = get_last_day(now)
try:
get_fiscal_year(now, company="_T")
except FiscalYearError:
docs = [{
"companies": [{
"company": "_T",
"parent": "_Test Fiscal",
"parentfield": "companies",
"parenttype": "Fiscal Year"
}],
"doctype": "Fiscal Year",
"year": "_Test Fiscal",
"year_end_date": get_year_ending(now),
"year_start_date": get_year_start(now)
}] + docs
docs = [{
"abbr": "_T",
"company_name": "_T",
"country": "United Kingdom",
"default_currency": "GBP",
"doctype": "Company",
"name": "_T"
}] + docs
for doc in docs:
try:
db_doc = frappe.get_doc(doc)
if 'Invoice' in db_doc.doctype:
db_doc.due_date = add_to_date(now, days=1)
db_doc.insert()
# Create GL Entries:
db_doc.submit()
else:
db_doc.insert()
except frappe.exceptions.DuplicateEntryError:
pass
def load_defcols(self):
self.company = frappe.get_doc('Company', '_T')
custom_report = frappe.get_doc('Report', 'Tax Detail')
self.default_columns, _ = custom_report.run_query_report(
filters={
'from_date': '2021-03-01',
'to_date': '2021-03-31',
'company': self.company.name,
'mode': 'run',
'report_name': 'Tax Detail'
}, user=frappe.session.user)
def rm_testdocs(self):
"Remove the Company and all data"
from erpnext.setup.doctype.company.company import create_transaction_deletion_request
create_transaction_deletion_request(self.company.name)
def test_report(self):
self.load_testdocs()
self.load_defcols()
report_name = save_custom_report(
'Tax Detail',
'_Test Tax Detail',
json.dumps({
'columns': self.default_columns,
'sections': {
'Box1':{'Filter0':{'type':'filter','filters':{'4':'VAT on Sales'}}},
'Box2':{'Filter0':{'type':'filter','filters':{'4':'Acquisition'}}},
'Box3':{'Box1':{'type':'section'},'Box2':{'type':'section'}},
'Box4':{'Filter0':{'type':'filter','filters':{'4':'VAT on Purchases'}}},
'Box5':{'Box3':{'type':'section'},'Box4':{'type':'section'}},
'Box6':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales'}}},
'Box7':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax'}}},
'Box8':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales','12':'EU'}}},
'Box9':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax','12':'EU'}}}
},
'show_detail': 1
}))
data = frappe.desk.query_report.run(report_name,
filters={
'from_date': self.from_date,
'to_date': self.to_date,
'company': self.company.name,
'mode': 'run',
'report_name': report_name
}, user=frappe.session.user)
self.assertListEqual(data.get('columns'), self.default_columns)
expected = (('Box1', 43.25), ('Box2', 0.0), ('Box3', 43.25), ('Box4', -85.28), ('Box5', -42.03),
('Box6', 825.0), ('Box7', -426.40), ('Box8', 0.0), ('Box9', 0.0))
exrow = iter(expected)
for row in data.get('result'):
if row.get('voucher_no') and not row.get('posting_date'):
label, value = next(exrow)
self.assertDictEqual(row, {'voucher_no': label, 'amount': value})
self.assertListEqual(data.get('report_summary'),
[{'label': label, 'datatype': 'Currency', 'value': value} for label, value in expected])
self.rm_testdocs()
def test_filter_match(self):
# None - treated as -inf number except range
self.assertTrue(filter_match(None, '!='))
self.assertTrue(filter_match(None, '<'))
self.assertTrue(filter_match(None, '<jjj'))
self.assertTrue(filter_match(None, ' : '))
self.assertTrue(filter_match(None, ':56'))
self.assertTrue(filter_match(None, ':de'))
self.assertFalse(filter_match(None, '3.4'))
self.assertFalse(filter_match(None, '='))
self.assertFalse(filter_match(None, '=3.4'))
self.assertFalse(filter_match(None, '>3.4'))
self.assertFalse(filter_match(None, ' <'))
self.assertFalse(filter_match(None, 'ew'))
self.assertFalse(filter_match(None, ' '))
self.assertFalse(filter_match(None, ' f :'))
# Numbers
self.assertTrue(filter_match(3.4, '3.4'))
self.assertTrue(filter_match(3.4, '.4'))
self.assertTrue(filter_match(3.4, '3'))
self.assertTrue(filter_match(-3.4, '< -3'))
self.assertTrue(filter_match(-3.4, '> -4'))
self.assertTrue(filter_match(3.4, '= 3.4 '))
self.assertTrue(filter_match(3.4, '!=4.5'))
self.assertTrue(filter_match(3.4, ' 3 : 4 '))
self.assertTrue(filter_match(0.0, ' : '))
self.assertFalse(filter_match(3.4, '=4.5'))
self.assertFalse(filter_match(3.4, ' = 3.4 '))
self.assertFalse(filter_match(3.4, '!=3.4'))
self.assertFalse(filter_match(3.4, '>6'))
self.assertFalse(filter_match(3.4, '<-4.5'))
self.assertFalse(filter_match(3.4, '4.5'))
self.assertFalse(filter_match(3.4, '5:9'))
# Strings
self.assertTrue(filter_match('ACC-SINV-2021-00001', 'SINV'))
self.assertTrue(filter_match('ACC-SINV-2021-00001', 'sinv'))
self.assertTrue(filter_match('ACC-SINV-2021-00001', '-2021'))
self.assertTrue(filter_match(' ACC-SINV-2021-00001', ' acc'))
self.assertTrue(filter_match('ACC-SINV-2021-00001', '=2021'))
self.assertTrue(filter_match('ACC-SINV-2021-00001', '!=zz'))
self.assertTrue(filter_match('ACC-SINV-2021-00001', '< zzz '))
self.assertTrue(filter_match('ACC-SINV-2021-00001', ' : sinv '))
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' sinv :'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' acc'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', '= 2021 '))
self.assertFalse(filter_match('ACC-SINV-2021-00001', '!=sinv'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' >'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', '>aa'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' <'))
self.assertFalse(filter_match('ACC-SINV-2021-00001', '< '))
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' ='))
self.assertFalse(filter_match('ACC-SINV-2021-00001', '='))
# Date - always match
self.assertTrue(filter_match(datetime.date(2021, 3, 19), ' kdsjkldfs '))

View File

@ -434,6 +434,16 @@
"onboard": 0,
"type": "Link"
},
{
"dependencies": "GL Entry",
"hidden": 0,
"is_query_report": 1,
"label": "Tax Detail",
"link_to": "Tax Detail",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "GL Entry",
"hidden": 0,
@ -442,6 +452,7 @@
"link_to": "DATEV",
"link_type": "Report",
"onboard": 0,
"only_for": "Germany",
"type": "Link"
},
{
@ -452,6 +463,7 @@
"link_to": "UAE VAT 201",
"link_type": "Report",
"onboard": 0,
"only_for": "United Arab Emirates",
"type": "Link"
},
{

View File

@ -102,8 +102,8 @@ frappe.ui.form.on("Purchase Order Item", {
}
});
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
setup: function() {
erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends erpnext.buying.BuyingController {
setup() {
this.frm.custom_make_buttons = {
'Purchase Receipt': 'Purchase Receipt',
'Purchase Invoice': 'Purchase Invoice',
@ -111,13 +111,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
'Payment Entry': 'Payment',
}
this._super();
super.setup();
},
}
refresh: function(doc, cdt, cdn) {
refresh(doc, cdt, cdn) {
var me = this;
this._super();
super.refresh();
var allow_receipt = false;
var is_drop_ship = false;
@ -223,9 +223,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
} else if(doc.docstatus===0) {
cur_frm.cscript.add_from_mappers();
}
},
}
get_items_from_open_material_requests: function() {
get_items_from_open_material_requests() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order_based_on_supplier",
args: {
@ -243,17 +243,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
},
get_query_method: "erpnext.stock.doctype.material_request.material_request.get_material_requests_based_on_supplier"
});
},
}
validate: function() {
validate() {
set_schedule_date(this.frm);
},
}
has_unsupplied_items: function() {
return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty)
},
has_unsupplied_items() {
return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty);
}
make_stock_entry: function() {
make_stock_entry() {
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
var me = this;
@ -368,9 +368,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
me.dialog.hide();
});
},
}
_make_rm_stock_entry: function(rm_items) {
_make_rm_stock_entry(rm_items) {
frappe.call({
method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry",
args: {
@ -383,31 +383,31 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
},
}
make_inter_company_order: function(frm) {
make_inter_company_order(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order",
frm: frm
});
},
}
make_purchase_receipt: function() {
make_purchase_receipt() {
frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
frm: cur_frm,
freeze_message: __("Creating Purchase Receipt ...")
})
},
}
make_purchase_invoice: function() {
make_purchase_invoice() {
frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
frm: cur_frm
})
},
}
add_from_mappers: function() {
add_from_mappers() {
var me = this;
this.frm.add_custom_button(__('Material Request'),
function() {
@ -513,13 +513,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
}
});
}, __("Tools"));
},
}
tc_name: function() {
tc_name() {
this.get_terms();
},
}
items_add: function(doc, cdt, cdn) {
items_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
if(doc.schedule_date) {
row.schedule_date = doc.schedule_date;
@ -527,13 +527,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
} else {
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
}
},
}
unhold_purchase_order: function(){
unhold_purchase_order(){
cur_frm.cscript.update_status("Resume", "Draft")
},
}
hold_purchase_order: function(){
hold_purchase_order(){
var me = this;
var d = new frappe.ui.Dialog({
title: __('Reason for Hold'),
@ -567,31 +567,31 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
}
});
d.show();
},
}
unclose_purchase_order: function(){
unclose_purchase_order(){
cur_frm.cscript.update_status('Re-open', 'Submitted')
},
}
close_purchase_order: function(){
close_purchase_order(){
cur_frm.cscript.update_status('Close', 'Closed')
},
}
delivered_by_supplier: function(){
delivered_by_supplier(){
cur_frm.cscript.update_status('Deliver', 'Delivered')
},
}
items_on_form_rendered: function() {
set_schedule_date(this.frm);
},
schedule_date: function() {
items_on_form_rendered() {
set_schedule_date(this.frm);
}
});
schedule_date() {
set_schedule_date(this.frm);
}
};
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
cur_frm.cscript.update_status= function(label, status){
frappe.call({

View File

@ -205,10 +205,10 @@ frappe.ui.form.on("Request for Quotation Supplier",{
})
erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({
refresh: function() {
erpnext.buying.RequestforQuotationController = class RequestforQuotationController extends erpnext.buying.BuyingController {
refresh() {
var me = this;
this._super();
super.refresh();
if (this.frm.doc.docstatus===0) {
this.frm.add_custom_button(__('Material Request'),
function() {
@ -302,17 +302,17 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
me.get_suppliers_button(me.frm);
}, __("Tools"));
}
},
}
calculate_taxes_and_totals: function() {
calculate_taxes_and_totals() {
return;
},
}
tc_name: function() {
tc_name() {
this.get_terms();
},
}
get_suppliers_button: function (frm) {
get_suppliers_button (frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
title: __("Get Suppliers"),
@ -410,8 +410,8 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
});
dialog.show();
},
});
}
};
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));

View File

@ -4,19 +4,19 @@
// attach required files
{% include 'erpnext/public/js/controllers/buying.js' %};
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
setup: function() {
erpnext.buying.SupplierQuotationController = class SupplierQuotationController extends erpnext.buying.BuyingController {
setup() {
this.frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order',
'Quotation': 'Quotation'
}
this._super();
},
super.setup();
}
refresh: function() {
refresh() {
var me = this;
this._super();
super.refresh();
if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
@ -77,25 +77,25 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
})
}, __("Get Items From"));
}
},
}
make_purchase_order: function() {
make_purchase_order() {
frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
frm: cur_frm
})
},
make_quotation: function() {
}
make_quotation() {
frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation",
frm: cur_frm
})
}
});
};
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
cur_frm.fields_dict['items'].grid.get_field('project').get_query =
function(doc, cdt, cdn) {

View File

@ -0,0 +1,73 @@
# Version 13.3.0 Release Notes
### Features & Enhancements
- Purchase receipt creation from purchase invoice ([#25126](https://github.com/frappe/erpnext/pull/25126))
- New Document Transaction Deletion ([#25354](https://github.com/frappe/erpnext/pull/25354))
- Employee Referral ([#24997](https://github.com/frappe/erpnext/pull/24997))
- Add Create Expense Claim button in Delivery Trip ([#25526](https://github.com/frappe/erpnext/pull/25526))
- Reduced rate of asset depreciation as per IT Act ([#25648](https://github.com/frappe/erpnext/pull/25648))
- Improve DATEV export ([#25238](https://github.com/frappe/erpnext/pull/25238))
- Add pick batch button ([#25413](https://github.com/frappe/erpnext/pull/25413))
- Enable custom field search on POS ([#25421](https://github.com/frappe/erpnext/pull/25421))
- New check field in subscriptions for (not) submitting invoices ([#25394](https://github.com/frappe/erpnext/pull/25394))
- Show POS reserved stock in stock projected qty report ([#25593](https://github.com/frappe/erpnext/pull/25593))
- e-way bill validity field ([#25555](https://github.com/frappe/erpnext/pull/25555))
- Significant reduction in time taken to save sales documents ([#25475](https://github.com/frappe/erpnext/pull/25475))
### Fixes
- Bank statement import via google sheet ([#25677](https://github.com/frappe/erpnext/pull/25677))
- Invoices not getting fetched during payment reconciliation ([#25598](https://github.com/frappe/erpnext/pull/25598))
- Error on applying TDS without party ([#25632](https://github.com/frappe/erpnext/pull/25632))
- Allow to cancel loan with cancelled repayment entry ([#25507](https://github.com/frappe/erpnext/pull/25507))
- Can't open general ledger from consolidated financial report ([#25542](https://github.com/frappe/erpnext/pull/25542))
- Add 'Partially Received' to Status drop-down list in Material Request ([#24857](https://github.com/frappe/erpnext/pull/24857))
- Updated item filters for material request ([#25531](https://github.com/frappe/erpnext/pull/25531))
- Added validation in stock entry to check duplicate serial nos ([#25611](https://github.com/frappe/erpnext/pull/25611))
- Update shopify api version ([#25600](https://github.com/frappe/erpnext/pull/25600))
- Dialog variable assignment after definition in POS ([#25680](https://github.com/frappe/erpnext/pull/25680))
- Added tax_types list ([#25587](https://github.com/frappe/erpnext/pull/25587))
- Include search fields in Project Link field query ([#25505](https://github.com/frappe/erpnext/pull/25505))
- Item stock levels displaying inconsistently ([#25506](https://github.com/frappe/erpnext/pull/25506))
- Change today to now to get data for reposting ([#25703](https://github.com/frappe/erpnext/pull/25703))
- Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet ([#25700](https://github.com/frappe/erpnext/pull/25700))
- Minor fixes in loan ([#25546](https://github.com/frappe/erpnext/pull/25546))
- Fieldname when updating docfield property ([#25516](https://github.com/frappe/erpnext/pull/25516))
- Use get_serial_nos for splitting ([#25590](https://github.com/frappe/erpnext/pull/25590))
- Show item's full name on hover over item in POS ([#25554](https://github.com/frappe/erpnext/pull/25554))
- Stock ledger entry created against draft stock entry ([#25540](https://github.com/frappe/erpnext/pull/25540))
- Incorrect expense account set in pos invoice ([#25543](https://github.com/frappe/erpnext/pull/25543))
- Stock balance and batch-wise balance history report showing different closing stock ([#25575](https://github.com/frappe/erpnext/pull/25575))
- Make strings translatable ([#25521](https://github.com/frappe/erpnext/pull/25521))
- Serial no changed after saving stock reconciliation ([#25541](https://github.com/frappe/erpnext/pull/25541))
- Ignore fraction difference while making round off gl entry ([#25438](https://github.com/frappe/erpnext/pull/25438))
- Sync shopify customer addresses ([#25481](https://github.com/frappe/erpnext/pull/25481))
- Total stock summary report not working ([#25551](https://github.com/frappe/erpnext/pull/25551))
- Rename field has not updated value of deposit and withdrawal fields ([#25545](https://github.com/frappe/erpnext/pull/25545))
- Unexpected keyword argument 'merge_logs' ([#25489](https://github.com/frappe/erpnext/pull/25489))
- Validation message of quality inspection in purchase receipt ([#25667](https://github.com/frappe/erpnext/pull/25667))
- Added is_stock_item filter ([#25530](https://github.com/frappe/erpnext/pull/25530))
- Fetch total stock at company in PO ([#25532](https://github.com/frappe/erpnext/pull/25532))
- Updated filters for process statement of accounts ([#25384](https://github.com/frappe/erpnext/pull/25384))
- Incorrect expense account set in pos invoice ([#25571](https://github.com/frappe/erpnext/pull/25571))
- Client script breaking while settings tax labels ([#25653](https://github.com/frappe/erpnext/pull/25653))
- Empty payment term column in accounts receivable report ([#25556](https://github.com/frappe/erpnext/pull/25556))
- Designation insufficient permission on lead doctype. ([#25331](https://github.com/frappe/erpnext/pull/25331))
- Force https for shopify webhook registration ([#25630](https://github.com/frappe/erpnext/pull/25630))
- Patch regional fields for old companies ([#25673](https://github.com/frappe/erpnext/pull/25673))
- Woocommerce order sync issue ([#25692](https://github.com/frappe/erpnext/pull/25692))
- Allow to receive same serial numbers multiple times ([#25471](https://github.com/frappe/erpnext/pull/25471))
- Update Allocated amount after Paid Amount is changed in PE ([#25515](https://github.com/frappe/erpnext/pull/25515))
- Updating Standard Notification's channel field ([#25564](https://github.com/frappe/erpnext/pull/25564))
- Report summary showing inflated values when values are accumulated in Group Company ([#25577](https://github.com/frappe/erpnext/pull/25577))
- UI fixes related to overflowing payment section ([#25652](https://github.com/frappe/erpnext/pull/25652))
- List invoices in Payment Reconciliation Payment ([#25524](https://github.com/frappe/erpnext/pull/25524))
- Ageing errors in PSOA ([#25490](https://github.com/frappe/erpnext/pull/25490))
- Prevent spurious defaults for items when making prec from dnote ([#25559](https://github.com/frappe/erpnext/pull/25559))
- Stock reconciliation getting time out error during submission ([#25557](https://github.com/frappe/erpnext/pull/25557))
- Timesheet filter date exclusive issue ([#25626](https://github.com/frappe/erpnext/pull/25626))
- Update cost center in the item table fetched from POS Profile ([#25609](https://github.com/frappe/erpnext/pull/25609))
- Updated modified time in purchase invoice to pull new fields ([#25678](https://github.com/frappe/erpnext/pull/25678))
- Stock and Accounts Settings form refactor ([#25534](https://github.com/frappe/erpnext/pull/25534))
- Payment amount showing in foreign currency ([#25292](https://github.com/frappe/erpnext/pull/25292))

View File

@ -0,0 +1,54 @@
# Version 13.4.0 Release Notes
### Features & Enhancements
- Multiple GST enhancement and fixes ([#25249](https://github.com/frappe/erpnext/pull/25249))
- Linking supplier with an item group for filtering items ([#25683](https://github.com/frappe/erpnext/pull/25683))
- Leave Policy Assignment Refactor ([#24327](https://github.com/frappe/erpnext/pull/24327))
- Dimension-wise Accounts Balance Report ([#25260](https://github.com/frappe/erpnext/pull/25260))
- Show net values in Party Accounts ([#25714](https://github.com/frappe/erpnext/pull/25714))
- Add pending qty section to batch/serial selector dialog ([#25519](https://github.com/frappe/erpnext/pull/25519))
- enhancements in Training Event ([#25782](https://github.com/frappe/erpnext/pull/25782))
- Refactored timesheet ([#25701](https://github.com/frappe/erpnext/pull/25701))
### Fixes
- Process Statement of Accounts formatting ([#25777](https://github.com/frappe/erpnext/pull/25777))
- Removed serial no validation for sales invoice ([#25817](https://github.com/frappe/erpnext/pull/25817))
- Fetch email id from dialog box in pos past order summary ([#25808](https://github.com/frappe/erpnext/pull/25808))
- Don't map set warehouse from delivery note to purchase receipt ([#25672](https://github.com/frappe/erpnext/pull/25672))
- Apply permission while selecting projects ([#25765](https://github.com/frappe/erpnext/pull/25765))
- Error on adding bank account to plaid ([#25658](https://github.com/frappe/erpnext/pull/25658))
- Set disable rounded total if it is globally enabled ([#25789](https://github.com/frappe/erpnext/pull/25789))
- Wrong amount on CR side in general ledger report for customer when different account currencies are involved ([#25654](https://github.com/frappe/erpnext/pull/25654))
- Stock move dialog duplicate submit actions (V13) ([#25486](https://github.com/frappe/erpnext/pull/25486))
- Cashflow mapper not showing data ([#25815](https://github.com/frappe/erpnext/pull/25815))
- Ignore rounding diff while importing JV using data import ([#25816](https://github.com/frappe/erpnext/pull/25816))
- Woocommerce order sync issue ([#25688](https://github.com/frappe/erpnext/pull/25688))
- Expected amount in pos closing payments table ([#25737](https://github.com/frappe/erpnext/pull/25737))
- Show only company addresses for ITC reversal entry ([#25867](https://github.com/frappe/erpnext/pull/25867))
- Timeout error while loading warehouse tree ([#25694](https://github.com/frappe/erpnext/pull/25694))
- Plaid Withdrawals and Deposits are recorded incorrectly ([#25784](https://github.com/frappe/erpnext/pull/25784))
- Return case for item with available qty equal to one ([#25760](https://github.com/frappe/erpnext/pull/25760))
- The status of repost item valuation showing In Progress since long time ([#25754](https://github.com/frappe/erpnext/pull/25754))
- Updated applicable charges form in landed cost voucher ([#25732](https://github.com/frappe/erpnext/pull/25732))
- Rearrange buttons for Company DocType ([#25617](https://github.com/frappe/erpnext/pull/25617))
- Show uom for item in selector dialog ([#25697](https://github.com/frappe/erpnext/pull/25697))
- Warehouse not found in stock entry ([#25776](https://github.com/frappe/erpnext/pull/25776))
- Use dictionary filter instead of list (bp #25874 pre-release) ([#25875](https://github.com/frappe/erpnext/pull/25875))
- Send emails on rfq submit ([#25695](https://github.com/frappe/erpnext/pull/25695))
- Cannot bypass e-invoicing for non gst item invoices ([#25759](https://github.com/frappe/erpnext/pull/25759))
- Validation message of quality inspection in purchase receipt ([#25666](https://github.com/frappe/erpnext/pull/25666))
- Dialog variable assignment after definition in POS ([#25681](https://github.com/frappe/erpnext/pull/25681))
- Wrong quantity after transaction for parallel stock transactions ([#25779](https://github.com/frappe/erpnext/pull/25779))
- Item Variant Details Report ([#25797](https://github.com/frappe/erpnext/pull/25797))
- Duplicate stock entry on multiple click ([#25742](https://github.com/frappe/erpnext/pull/25742))
- Bank statement import via google sheet ([#25676](https://github.com/frappe/erpnext/pull/25676))
- Change today to now to get data for reposting ([#25702](https://github.com/frappe/erpnext/pull/25702))
- Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet ([#25698](https://github.com/frappe/erpnext/pull/25698))
- Ageing error in PSOA ([#25857](https://github.com/frappe/erpnext/pull/25857))
- Breaking cost center validation ([#25660](https://github.com/frappe/erpnext/pull/25660))
- Project filter for Kanban Board ([#25744](https://github.com/frappe/erpnext/pull/25744))
- Show allow zero valuation only when auto checked ([#25778](https://github.com/frappe/erpnext/pull/25778))
- Missing cost center message on creating gl entries ([#25755](https://github.com/frappe/erpnext/pull/25755))
- Address template with upper filter throws jinja error ([#25756](https://github.com/frappe/erpnext/pull/25756))

View File

@ -0,0 +1,54 @@
# Version 13.5.0 Release Notes
### Features & Enhancements
- Tax deduction against advance payments ([#25831](https://github.com/frappe/erpnext/pull/25831))
- Cost-center wise period closing entry ([#25766](https://github.com/frappe/erpnext/pull/25766))
- Create Quality Inspections from account and stock documents ([#25221](https://github.com/frappe/erpnext/pull/25221))
- Item Taxes based on net rate ([#25961](https://github.com/frappe/erpnext/pull/25961))
- Enable/disable gl entry posting for change given in pos ([#25822](https://github.com/frappe/erpnext/pull/25822))
- Add Inactive status to Employee ([#26029](https://github.com/frappe/erpnext/pull/26029))
- Added check box to combine items with same BOM ([#25478](https://github.com/frappe/erpnext/pull/25478))
- Item Tax Templates for Germany ([#25858](https://github.com/frappe/erpnext/pull/25858))
- Refactored leave balance report ([#25771](https://github.com/frappe/erpnext/pull/25771))
- Refactored Vehicle Expenses Report ([#25727](https://github.com/frappe/erpnext/pull/25727))
- Refactored maintenance schedule and visit document ([#25358](https://github.com/frappe/erpnext/pull/25358))
### Fixes
- Cannot add same item with different rates ([#25849](https://github.com/frappe/erpnext/pull/25849))
- Show only company addresses for ITC reversal entry ([#25866](https://github.com/frappe/erpnext/pull/25866))
- Hiding Rounding Adjustment field ([#25380](https://github.com/frappe/erpnext/pull/25380))
- Auto tax calculations in Payment Entry ([#26055](https://github.com/frappe/erpnext/pull/26055))
- Not able to select the item code in work order ([#25915](https://github.com/frappe/erpnext/pull/25915))
- Cannot reset plaid link for a bank account ([#25869](https://github.com/frappe/erpnext/pull/25869))
- Student invalid password reset link ([#25826](https://github.com/frappe/erpnext/pull/25826))
- Multiple pos issues ([#25928](https://github.com/frappe/erpnext/pull/25928))
- Add Product Bundles to POS ([#25860](https://github.com/frappe/erpnext/pull/25860))
- Enable Parallel tests ([#25862](https://github.com/frappe/erpnext/pull/25862))
- Service item check on e-Invoicing ([#25986](https://github.com/frappe/erpnext/pull/25986))
- Choose correct Salary Structure Assignment when getting data for formula eval ([#25981](https://github.com/frappe/erpnext/pull/25981))
- Ignore internal transfer invoices from GST Reports ([#25969](https://github.com/frappe/erpnext/pull/25969))
- Taxable value for invoices with additional discount ([#26056](https://github.com/frappe/erpnext/pull/26056))
- Validate negative allocated amount in Payment Entry ([#25799](https://github.com/frappe/erpnext/pull/25799))
- Allow all System Managers to delete company transactions ([#25834](https://github.com/frappe/erpnext/pull/25834))
- Wrong round off gl entry posted in case of purchase invoice ([#25775](https://github.com/frappe/erpnext/pull/25775))
- Use dictionary filter instead of list ([#25874](https://github.com/frappe/erpnext/pull/25874))
- Ageing error in PSOA ([#25855](https://github.com/frappe/erpnext/pull/25855))
- On click of duplicate button system has not copied the difference account ([#25988](https://github.com/frappe/erpnext/pull/25988))
- Assign Product Bundle's conversion_factor to Pack… ([#25840](https://github.com/frappe/erpnext/pull/25840))
- Rename Loan Management workspace to Loans ([#25856](https://github.com/frappe/erpnext/pull/25856))
- Fix stock quantity calculation when negative_stock_allowe… ([#25859](https://github.com/frappe/erpnext/pull/25859))
- Update cost center from pos profile ([#25971](https://github.com/frappe/erpnext/pull/25971))
- Ensure website theme is applied correctly ([#25863](https://github.com/frappe/erpnext/pull/25863))
- Only display GST card in Accounting Workspace if it's in India ([#26000](https://github.com/frappe/erpnext/pull/26000))
- Incorrect gstin fetched incase of branch company address ([#25841](https://github.com/frappe/erpnext/pull/25841))
- Sort account balances by account name ([#26009](https://github.com/frappe/erpnext/pull/26009))
- Custom conversion factor field not mapped from job card to stock entry ([#25956](https://github.com/frappe/erpnext/pull/25956))
- Chart of accounts importer always error ([#25882](https://github.com/frappe/erpnext/pull/25882))
- Create POS Invoice for Product Bundles ([#25847](https://github.com/frappe/erpnext/pull/25847))
- Wrap dates in getdate for leave application ([#25899](https://github.com/frappe/erpnext/pull/25899))
- Closing entry shows incorrect expected amount ([#25868](https://github.com/frappe/erpnext/pull/25868))
- Add Hold status column in the Issue Summary Report ([#25828](https://github.com/frappe/erpnext/pull/25828))
- Rendering of broken image on pos ([#25872](https://github.com/frappe/erpnext/pull/25872))
- Timeout error in the repost item valuation ([#25854](https://github.com/frappe/erpnext/pull/25854))

View File

@ -0,0 +1,72 @@
# Version 13.6.0 Release Notes
### Features & Enhancements
- Job Card Enhancements ([#24523](https://github.com/frappe/erpnext/pull/24523))
- Implement multi-account selection in General Ledger([#26044](https://github.com/frappe/erpnext/pull/26044))
- Fetching of qty as per received qty from PR to PI ([#26184](https://github.com/frappe/erpnext/pull/26184))
- Subcontract code refactor and enhancement ([#25878](https://github.com/frappe/erpnext/pull/25878))
- Employee Grievance ([#25705](https://github.com/frappe/erpnext/pull/25705))
- Add Inactive status to Employee ([#26030](https://github.com/frappe/erpnext/pull/26030))
- Incorrect valuation rate report for serialized items ([#25696](https://github.com/frappe/erpnext/pull/25696))
- Update cost updates operation time and hour rates in BOM ([#25891](https://github.com/frappe/erpnext/pull/25891))
### Fixes
- Precision rate for packed items in internal transfers ([#26046](https://github.com/frappe/erpnext/pull/26046))
- User is not able to change item tax template ([#26176](https://github.com/frappe/erpnext/pull/26176))
- Insufficient permission for Dunning error ([#26092](https://github.com/frappe/erpnext/pull/26092))
- Validate Product Bundle for existing transactions before deletion ([#25978](https://github.com/frappe/erpnext/pull/25978))
- Auto unlink warehouse from item on delete ([#26073](https://github.com/frappe/erpnext/pull/26073))
- Employee Inactive status implications ([#26245](https://github.com/frappe/erpnext/pull/26245))
- Fetch batch items in stock reconciliation ([#26230](https://github.com/frappe/erpnext/pull/26230))
- Disabled cancellation for sales order if linked to drafted sales invoice ([#26125](https://github.com/frappe/erpnext/pull/26125))
- Sort website products by weightage mentioned in Item master ([#26134](https://github.com/frappe/erpnext/pull/26134))
- Added freeze when trying to stop work order (#26192) ([#26196](https://github.com/frappe/erpnext/pull/26196))
- Accounting Dimensions for payroll entry accrual Journal Entry ([#26083](https://github.com/frappe/erpnext/pull/26083))
- Staffing plan vacancies data type issue ([#25941](https://github.com/frappe/erpnext/pull/25941))
- Unable to enter score in Assessment Result details grid ([#25945](https://github.com/frappe/erpnext/pull/25945))
- Report Subcontracted Raw Materials to be Transferred ([#26011](https://github.com/frappe/erpnext/pull/26011))
- Label for enabling ledger posting of change amount ([#26070](https://github.com/frappe/erpnext/pull/26070))
- Training event ([#26071](https://github.com/frappe/erpnext/pull/26071))
- Rate not able to change in purchase order ([#26122](https://github.com/frappe/erpnext/pull/26122))
- Error while fetching item taxes ([#26220](https://github.com/frappe/erpnext/pull/26220))
- Check for duplicate payment terms in Payment Term Template ([#26003](https://github.com/frappe/erpnext/pull/26003))
- Removed values out of sync validation from stock transactions ([#26229](https://github.com/frappe/erpnext/pull/26229))
- Fetching employee in payroll entry ([#26269](https://github.com/frappe/erpnext/pull/26269))
- Filter Cost Center and Project drop-down lists by Company ([#26045](https://github.com/frappe/erpnext/pull/26045))
- Website item group logic for product listing in Item Group pages ([#26170](https://github.com/frappe/erpnext/pull/26170))
- Chart not visible for First Response Time reports ([#26032](https://github.com/frappe/erpnext/pull/26032))
- Incorrect billed qty in Sales Order analytics ([#26095](https://github.com/frappe/erpnext/pull/26095))
- Material request and supplier quotation not linked if supplier quotation created from supplier portal ([#26023](https://github.com/frappe/erpnext/pull/26023))
- Update leave allocation after submit ([#26191](https://github.com/frappe/erpnext/pull/26191))
- Taxes on Internal Transfer payment entry ([#26188](https://github.com/frappe/erpnext/pull/26188))
- Precision rate for packed items (bp #26046) ([#26217](https://github.com/frappe/erpnext/pull/26217))
- Fixed rounding off ordered percent to 100 in condition ([#26152](https://github.com/frappe/erpnext/pull/26152))
- Sanctioned loan amount limit check ([#26108](https://github.com/frappe/erpnext/pull/26108))
- Purchase receipt gl entries with same item code ([#26202](https://github.com/frappe/erpnext/pull/26202))
- Taxable value for invoices with additional discount ([#25906](https://github.com/frappe/erpnext/pull/25906))
- Correct South Africa VAT Rate (Updated) ([#25894](https://github.com/frappe/erpnext/pull/25894))
- Remove response_by and resolution_by if sla is removed ([#25997](https://github.com/frappe/erpnext/pull/25997))
- POS loyalty card alignment ([#26051](https://github.com/frappe/erpnext/pull/26051))
- Flaky test for Report Subcontracted Raw materials to be transferred ([#26043](https://github.com/frappe/erpnext/pull/26043))
- Export invoices not visible in GSTR-1 report ([#26143](https://github.com/frappe/erpnext/pull/26143))
- Account filter not working with accounting dimension filter ([#26211](https://github.com/frappe/erpnext/pull/26211))
- Allow to select group warehouse while downloading materials from production plan ([#26126](https://github.com/frappe/erpnext/pull/26126))
- Added freeze when trying to stop work order ([#26192](https://github.com/frappe/erpnext/pull/26192))
- Time out while submit / cancel the stock transactions with more than 50 Items ([#26081](https://github.com/frappe/erpnext/pull/26081))
- Address Card issues in e-commerce ([#26187](https://github.com/frappe/erpnext/pull/26187))
- Error while booking deferred revenue ([#26195](https://github.com/frappe/erpnext/pull/26195))
- Eliminate repeat creation of HSN codes ([#25947](https://github.com/frappe/erpnext/pull/25947))
- Opening invoices can alter profit and loss of a closed year ([#25951](https://github.com/frappe/erpnext/pull/25951))
- Payroll entry employee detail issue ([#25968](https://github.com/frappe/erpnext/pull/25968))
- Auto tax calculations in Payment Entry ([#26037](https://github.com/frappe/erpnext/pull/26037))
- Use pos invoice item name as unique identifier ([#26198](https://github.com/frappe/erpnext/pull/26198))
- Billing address not fetched in Purchase Invoice ([#26100](https://github.com/frappe/erpnext/pull/26100))
- Timeout while cancelling stock reconciliation ([#26098](https://github.com/frappe/erpnext/pull/26098))
- Status indicator for delivery notes ([#26062](https://github.com/frappe/erpnext/pull/26062))
- Unable to enter score in Assessment Result details grid ([#26031](https://github.com/frappe/erpnext/pull/26031))
- Too many writes while renaming company abbreviation ([#26203](https://github.com/frappe/erpnext/pull/26203))
- Chart not visible for First Response Time reports ([#26185](https://github.com/frappe/erpnext/pull/26185))
- Job applicant link issue ([#25934](https://github.com/frappe/erpnext/pull/25934))
- Fetch preferred shipping address (bp #26132) ([#26201](https://github.com/frappe/erpnext/pull/26201))

View File

@ -330,9 +330,15 @@ class SellingController(StockController):
# For internal transfers use incoming rate as the valuation rate
if self.is_internal_transfer():
rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
if d.rate != rate:
d.rate = rate
if d.doctype == "Packed Item":
incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision('incoming_rate'))
if d.incoming_rate != incoming_rate:
d.incoming_rate = incoming_rate
else:
rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
if d.rate != rate:
d.rate = rate
d.discount_percentage = 0
d.discount_amount = 0
frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")

View File

@ -11,7 +11,7 @@ from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
import erpnext
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
from erpnext.accounts.utils import check_if_stock_and_account_balance_synced, get_fiscal_year
from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock import get_warehouse_account_map
from erpnext.stock.stock_ledger import get_valuation_rate
@ -497,9 +497,6 @@ class StockController(AccountsController):
})
if future_sle_exists(args):
create_repost_item_valuation_entry(args)
elif not is_reposting_pending():
check_if_stock_and_account_balance_synced(self.posting_date,
self.company, self.doctype, self.name)
@frappe.whitelist()
def make_quality_inspections(doctype, docname, items):

View File

@ -4,8 +4,8 @@
frappe.provide("erpnext");
cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function () {
erpnext.LeadController = class LeadController extends frappe.ui.form.Controller {
setup () {
this.frm.make_methods = {
'Customer': this.make_customer,
'Quotation': this.make_quotation,
@ -13,9 +13,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
};
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
},
}
onload: function () {
onload () {
this.frm.set_query("customer", function (doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" }
});
@ -27,9 +27,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
this.frm.set_query("contact_by", function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
});
},
}
refresh: function () {
refresh () {
let doc = this.frm.doc;
erpnext.toggle_naming_series();
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
@ -45,47 +45,47 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
} else {
frappe.contacts.clear_address_and_contact(this.frm);
}
},
}
make_customer: function () {
make_customer () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm
})
},
}
make_opportunity: function () {
make_opportunity () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
frm: cur_frm
})
},
}
make_quotation: function () {
make_quotation () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_quotation",
frm: cur_frm
})
},
}
organization_lead: function () {
organization_lead () {
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
},
}
company_name: function () {
company_name () {
if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
}
},
}
contact_date: function () {
contact_date () {
if (this.frm.doc.contact_date) {
let d = moment(this.frm.doc.contact_date);
d.add(1, "day");
this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
}
}
});
};
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));

View File

@ -145,8 +145,8 @@ frappe.ui.form.on("Opportunity", {
})
// TODO commonify this code
erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
onload: function() {
erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
onload() {
if(!this.frm.doc.status) {
frm.set_value('status', 'Open');
@ -159,9 +159,9 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
}
this.setup_queries();
},
}
setup_queries: function() {
setup_queries() {
var me = this;
if(this.frm.fields_dict.contact_by.df.options.match(/^User/)) {
@ -185,17 +185,17 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
else if (me.frm.doc.opportunity_from == "Customer") {
me.frm.set_query('party_name', erpnext.queries['customer']);
}
},
}
create_quotation: function() {
create_quotation() {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
frm: cur_frm
})
}
});
};
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
@ -213,4 +213,4 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
}
})
}
}
}

View File

@ -91,4 +91,4 @@ def check_activity_exists(enrollment, content_type, content):
if activity:
return activity[0].name
else:
return None
return None

View File

@ -72,8 +72,8 @@ frappe.ui.form.on('Student Attendance Tool', {
});
education.StudentsEditor = Class.extend({
init: function(frm, wrapper, students) {
education.StudentsEditor = class StudentsEditor {
constructor(frm, wrapper, students) {
this.wrapper = wrapper;
this.frm = frm;
if(students.length > 0) {
@ -81,8 +81,8 @@ education.StudentsEditor = Class.extend({
} else {
this.show_empty_state();
}
},
make: function(frm, students) {
}
make(frm, students) {
var me = this;
$(this.wrapper).empty();
@ -173,13 +173,13 @@ education.StudentsEditor = Class.extend({
});
$(htmls.join("")).appendTo(me.wrapper);
},
}
show_empty_state: function() {
show_empty_state() {
$(this.wrapper).html(
`<div class="text-center text-muted" style="line-height: 100px;">
${__("No Students in")} ${this.frm.doc.student_group}
</div>`
);
}
});
};

View File

@ -9,14 +9,14 @@ frappe.ui.form.on('Exercise Type', {
}
});
erpnext.ExerciseEditor = Class.extend({
init: function(frm, wrapper) {
erpnext.ExerciseEditor = class ExerciseEditor {
constructor(frm, wrapper) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, wrapper);
},
}
make: function(frm, wrapper) {
make(frm, wrapper) {
$(this.wrapper).empty();
this.exercise_toolbar = $('<p>\
@ -38,9 +38,9 @@ erpnext.ExerciseEditor = Class.extend({
this.make_cards(frm);
this.make_buttons(frm);
}
},
}
make_cards: function(frm) {
make_cards(frm) {
var me = this;
$(me.exercise_cards).empty();
@ -60,9 +60,9 @@ erpnext.ExerciseEditor = Class.extend({
</div>
</div>`, {image_src: step.image, title: step.title, description: step.description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
});
},
}
make_buttons: function(frm) {
make_buttons(frm) {
let me = this;
$('.btn-edit').on('click', function() {
let id = $(this).attr('data-id');
@ -82,9 +82,9 @@ erpnext.ExerciseEditor = Class.extend({
frm.dirty();
}, 300);
});
},
}
show_add_card_dialog: function(frm) {
show_add_card_dialog(frm) {
let me = this;
let d = new frappe.ui.Dialog({
title: __('Add Exercise Step'),
@ -137,9 +137,9 @@ erpnext.ExerciseEditor = Class.extend({
primary_action_label: __('Add')
});
d.show();
},
}
show_edit_card_dialog: function(frm, id) {
show_edit_card_dialog(frm, id) {
let new_dialog = new frappe.ui.Dialog({
title: __("Edit Exercise Step"),
fields: [
@ -183,4 +183,4 @@ erpnext.ExerciseEditor = Class.extend({
});
new_dialog.show();
}
});
};

View File

@ -40,7 +40,6 @@ class Patient(Document):
customer.customer_group = self.customer_group
if self.territory:
customer.territory = self.territory
customer.customer_name = self.patient_name
customer.default_price_list = self.default_price_list
customer.default_currency = self.default_currency

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe
import unittest
import json
from frappe.utils import getdate
from frappe.utils import getdate, strip_html
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
class TestPatientHistorySettings(unittest.TestCase):
@ -44,9 +44,9 @@ class TestPatientHistorySettings(unittest.TestCase):
self.assertTrue(medical_rec)
medical_rec = frappe.get_doc("Patient Medical Record", medical_rec)
expected_subject = "<b>Date: </b>{0}<br><b>Rating: </b>3<br><b>Feedback: </b>Test Patient History Settings<br>".format(
expected_subject = "Date: {0}Rating: 3Feedback: Test Patient History Settings".format(
frappe.utils.format_date(getdate()))
self.assertEqual(medical_rec.subject, expected_subject)
self.assertEqual(strip_html(medical_rec.subject), expected_subject)
self.assertEqual(medical_rec.patient, patient)
self.assertEqual(medical_rec.communication_date, getdate())
@ -101,4 +101,4 @@ def create_doc(patient):
}).insert()
doc.submit()
return doc
return doc

View File

@ -15,10 +15,11 @@ app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
develop_version = '13.x.x-develop'
app_include_js = "/assets/js/erpnext.min.js"
app_include_css = "/assets/css/erpnext.css"
web_include_js = "/assets/js/erpnext-web.min.js"
web_include_css = "/assets/css/erpnext-web.css"
app_include_js = "erpnext.bundle.js"
app_include_css = "erpnext.bundle.css"
web_include_js = "erpnext-web.bundle.js"
web_include_css = "erpnext-web.bundle.css"
email_css = "email_erpnext.bundle.css"
doctype_js = {
"Address": "public/js/address.js",
@ -227,6 +228,7 @@ standard_queries = {
doc_events = {
"*": {
"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
"on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record",
"on_update_after_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record",
"on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record"
@ -241,6 +243,9 @@ doc_events = {
"on_update": ["erpnext.hr.doctype.employee.employee.update_user_permissions",
"erpnext.portal.utils.set_default_role"]
},
"Communication": {
"on_update": "erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time"
},
("Sales Taxes and Charges Template", 'Price List'): {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"
},
@ -331,8 +336,8 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status",
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders"
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
"erpnext.support.doctype.service_level_agreement.service_level_agreement.set_service_level_agreement_variance"
],
"hourly_long": [
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"

View File

@ -11,5 +11,5 @@ cur_frm.cscript.onload = function(doc, cdt, cdn) {
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}
}

View File

@ -15,6 +15,7 @@ class Attendance(Document):
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
self.validate_attendance_date()
self.validate_duplicate_record()
self.validate_employee_status()
self.check_leave_record()
def validate_attendance_date(self):
@ -38,6 +39,10 @@ class Attendance(Document):
frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
frappe.bold(self.employee), frappe.bold(self.attendance_date)))
def validate_employee_status(self):
if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
def check_leave_record(self):
leave_record = frappe.db.sql("""
select leave_type, half_day, half_day_date

View File

@ -21,6 +21,9 @@ frappe.listview_settings['Attendance'] = {
label: __('For Employee'),
fieldtype: 'Link',
options: 'Employee',
get_query: () => {
return {query: "erpnext.controllers.queries.employee_query"}
},
reqd: 1,
onchange: function() {
dialog.set_df_property("unmarked_days", "hidden", 1);

View File

@ -2,8 +2,8 @@
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.hr");
erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
setup: function() {
erpnext.hr.EmployeeController = class EmployeeController extends frappe.ui.form.Controller {
setup() {
this.frm.fields_dict.user_id.get_query = function(doc, cdt, cdn) {
return {
query: "frappe.core.doctype.user.user.user_query",
@ -12,30 +12,30 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
}
this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.employee_query"} }
},
}
refresh: function() {
refresh() {
var me = this;
erpnext.toggle_naming_series();
},
}
date_of_birth: function() {
date_of_birth() {
return cur_frm.call({
method: "get_retirement_date",
args: {date_of_birth: this.frm.doc.date_of_birth}
});
},
}
salutation: function() {
salutation() {
if(this.frm.doc.salutation) {
this.frm.set_value("gender", {
"Mr": "Male",
"Ms": "Female"
}[this.frm.doc.salutation]);
}
},
}
});
};
frappe.ui.form.on('Employee',{
setup: function(frm) {
frm.set_query("leave_policy", function() {

View File

@ -68,13 +68,13 @@ erpnext.employee_attendance_tool = {
}
}
erpnext.MarkedEmployee = Class.extend({
init: function(frm, wrapper, employee) {
erpnext.MarkedEmployee = class MarkedEmployee {
constructor(frm, wrapper, employee) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, employee);
},
make: function(frm, employee) {
}
make(frm, employee) {
var me = this;
$(this.wrapper).empty();
@ -104,16 +104,16 @@ erpnext.MarkedEmployee = Class.extend({
})).appendTo(row);
});
}
});
};
erpnext.EmployeeSelector = Class.extend({
init: function(frm, wrapper, employee) {
erpnext.EmployeeSelector = class EmployeeSelector {
constructor(frm, wrapper, employee) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, employee);
},
make: function(frm, employee) {
}
make(frm, employee) {
var me = this;
$(this.wrapper).empty();
@ -266,6 +266,6 @@ erpnext.EmployeeSelector = Class.extend({
mark_employee_toolbar.appendTo($(this.wrapper));
}
});
};

View File

@ -9,16 +9,18 @@
"first_name",
"last_name",
"full_name",
"email",
"contact_no",
"resume",
"resume_link",
"column_break_6",
"date",
"status",
"for_designation",
"referral_details_section",
"email",
"contact_no",
"resume_link",
"column_break_12",
"current_employer",
"current_job_title",
"resume",
"referrer_details_section",
"referrer",
"referrer_name",
@ -189,12 +191,21 @@
"label": "Referral Bonus Payment Status",
"options": "\nUnpaid\nPaid",
"read_only": 1
},
{
"fieldname": "referral_details_section",
"fieldtype": "Section Break",
"label": "Referral Details"
},
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2021-04-26 21:21:38.094086",
"modified": "2021-05-04 17:03:26.134560",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Referral",

View File

@ -23,7 +23,7 @@ class HolidayList(Document):
last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,])
for i, d in enumerate(date_list):
ch = self.append('holidays', {})
ch.description = self.weekly_off
ch.description = _(self.weekly_off)
ch.holiday_date = d
ch.weekly_off = 1
ch.idx = last_idx + i + 1

View File

@ -5,7 +5,6 @@ import unittest
from frappe.utils import nowdate, add_months, getdate, add_days
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
class TestLeaveAllocation(unittest.TestCase):
@classmethod
def setUpClass(cls):

View File

@ -103,4 +103,4 @@ var set_total_estimated_budget = function(frm) {
})
frm.set_value('total_estimated_budget', estimated_budget);
}
}
};

View File

@ -5,19 +5,19 @@
frappe.provide("erpnext.hr");
erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
onload: function() {
erpnext.hr.AttendanceControlPanel = class AttendanceControlPanel extends frappe.ui.form.Controller {
onload() {
this.frm.set_value("att_fr_date", frappe.datetime.get_today());
this.frm.set_value("att_to_date", frappe.datetime.get_today());
},
}
refresh: function() {
refresh() {
this.frm.disable_save();
this.show_upload();
this.setup_import_progress();
},
}
get_template:function() {
get_template() {
if(!this.frm.doc.att_fr_date || !this.frm.doc.att_to_date) {
frappe.msgprint(__("Attendance From Date and Attendance To Date is mandatory"));
return;
@ -28,7 +28,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
from_date: this.frm.doc.att_fr_date,
to_date: this.frm.doc.att_to_date,
});
},
}
show_upload() {
var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
@ -36,7 +36,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
wrapper: $wrapper,
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
});
},
}
setup_import_progress() {
var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty();
@ -64,6 +64,6 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
}
});
}
})
}
cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm});

View File

@ -47,7 +47,7 @@ def get_data(filters, leave_types):
user = frappe.session.user
conditions = get_conditions(filters)
active_employees = frappe.get_all("Employee",
active_employees = frappe.get_list("Employee",
filters=conditions,
fields=["name", "employee_name", "department", "user_id", "leave_approver"])
@ -72,4 +72,4 @@ def get_data(filters, leave_types):
data.append(row)
return data
return data

View File

@ -57,10 +57,10 @@ def execute(filters=None):
data = []
leave_types = frappe.db.get_list("Leave Type")
leave_list = None
if filters.summarized_view:
leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
leave_list = [d[0] + ":Float:120" for d in leave_types]
leave_list = [d.name + ":Float:120" for d in leave_types]
columns.extend(leave_list)
columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
@ -72,11 +72,11 @@ def execute(filters=None):
if (att_map_set & emp_map_set):
parameter_row = ["<b>"+ parameter + "</b>"] + ['' for day in range(filters["total_days_in_month"] + 2)]
data.append(parameter_row)
record, emp_att_data = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list)
record, emp_att_data = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=leave_types)
emp_att_map.update(emp_att_data)
data += record
else:
record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list)
record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=leave_types)
data += record
chart_data = get_chart_data(emp_att_map, days)
@ -126,7 +126,7 @@ def get_chart_data(emp_att_map, days):
return chart
def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=None):
def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=None):
record = []
emp_att_map = {}
@ -204,9 +204,9 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho
else:
leaves[d.leave_type] = d.count
for d in leave_list:
if d in leaves:
row.append(leaves[d])
for d in leave_types:
if d.name in leaves:
row.append(leaves[d.name])
else:
row.append("0.0")

View File

@ -41,9 +41,9 @@ frappe.ui.form.on('Maintenance Schedule', {
})
// TODO commonify this code
erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
refresh: function () {
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer' };
erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frappe.ui.form.Controller {
refresh() {
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
var me = this;
@ -138,33 +138,29 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
}, __('Create'));
}
}
},
}
start_date: function (doc, cdt, cdn) {
start_date(doc, cdt, cdn) {
this.set_no_of_visits(doc, cdt, cdn);
},
}
end_date: function (doc, cdt, cdn) {
end_date(doc, cdt, cdn) {
this.set_no_of_visits(doc, cdt, cdn);
},
}
periodicity: function (doc, cdt, cdn) {
periodicity(doc, cdt, cdn) {
this.set_no_of_visits(doc, cdt, cdn);
}
},
no_of_visits: function (doc, cdt, cdn) {
this.set_no_of_visits(doc, cdt, cdn);
},
set_no_of_visits: function (doc, cdt, cdn) {
set_no_of_visits(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
let me = this;
if (item.start_date && item.periodicity) {
me.frm.call('validate_end_date_visits');
}
},
});
}
};
$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({ frm: cur_frm }));
extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));

View File

@ -64,9 +64,9 @@ frappe.ui.form.on('Maintenance Visit', {
})
// TODO commonify this code
erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
refresh: function () {
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer' };
erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui.form.Controller {
refresh() {
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
var me = this;
@ -119,7 +119,7 @@ erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
})
}, __("Get Items From"));
}
},
});
}
};
$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({ frm: cur_frm }));
extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm}));

View File

@ -325,8 +325,7 @@ frappe.ui.form.on("BOM", {
freeze: true,
args: {
update_parent: true,
from_child_bom:false,
save: frm.doc.docstatus === 1 ? true : false
from_child_bom:false
},
callback: function(r) {
refresh_field("items");
@ -359,16 +358,16 @@ frappe.ui.form.on("BOM", {
}
});
erpnext.bom.BomController = erpnext.TransactionController.extend({
conversion_rate: function(doc) {
erpnext.bom.BomController = class BomController extends erpnext.TransactionController {
conversion_rate(doc) {
if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0);
} else {
erpnext.bom.update_cost(doc);
}
},
}
item_code: function(doc, cdt, cdn){
item_code(doc, cdt, cdn){
var scrap_items = false;
var child = locals[cdt][cdn];
if (child.doctype == 'BOM Scrap Item') {
@ -380,19 +379,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
}
get_bom_material_detail(doc, cdt, cdn, scrap_items);
},
}
buying_price_list: function(doc) {
buying_price_list(doc) {
this.apply_price_list();
},
}
plc_conversion_rate: function(doc) {
plc_conversion_rate(doc) {
if (!this.in_apply_price_list) {
this.apply_price_list(null, true);
}
},
}
conversion_factor: function(doc, cdt, cdn) {
conversion_factor(doc, cdt, cdn) {
if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
@ -401,10 +400,10 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
this.toggle_conversion_factor(item);
this.frm.events.update_cost(this.frm);
}
},
});
}
};
$.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
extend_cscript(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
cur_frm.cscript.hour_rate = function(doc) {
erpnext.bom.calculate_op_cost(doc);
@ -654,4 +653,4 @@ frappe.ui.form.on("BOM", "with_operations", function(frm) {
if(!cint(frm.doc.with_operations)) {
frm.set_value("operations", []);
}
});
});

View File

@ -1,7 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from typing import List
from collections import deque
import frappe, erpnext
from frappe.utils import cint, cstr, flt, today
from frappe import _
@ -16,14 +17,85 @@ from frappe.model.mapper import get_mapped_doc
import functools
from six import string_types
from operator import itemgetter
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
}
class BOMTree:
"""Full tree representation of a BOM"""
# specifying the attributes to save resources
# ref: https://docs.python.org/3/reference/datamodel.html#slots
__slots__ = ["name", "child_items", "is_bom", "item_code", "exploded_qty", "qty"]
def __init__(self, name: str, is_bom: bool = True, exploded_qty: float = 1.0, qty: float = 1) -> None:
self.name = name # name of node, BOM number if is_bom else item_code
self.child_items: List["BOMTree"] = [] # list of child items
self.is_bom = is_bom # true if the node is a BOM and not a leaf item
self.item_code: str = None # item_code associated with node
self.qty = qty # required unit quantity to make one unit of parent item.
self.exploded_qty = exploded_qty # total exploded qty required for making root of tree.
if not self.is_bom:
self.item_code = self.name
else:
self.__create_tree()
def __create_tree(self):
bom = frappe.get_cached_doc("BOM", self.name)
self.item_code = bom.item
for item in bom.get("items", []):
qty = item.qty / bom.quantity # quantity per unit
exploded_qty = self.exploded_qty * qty
if item.bom_no:
child = BOMTree(item.bom_no, exploded_qty=exploded_qty, qty=qty)
self.child_items.append(child)
else:
self.child_items.append(
BOMTree(item.item_code, is_bom=False, exploded_qty=exploded_qty, qty=qty)
)
def level_order_traversal(self) -> List["BOMTree"]:
"""Get level order traversal of tree.
E.g. for following tree the traversal will return list of nodes in order from top to bottom.
BOM:
- SubAssy1
- item1
- item2
- SubAssy2
- item3
- item4
returns = [SubAssy1, item1, item2, SubAssy2, item3, item4]
"""
traversal = []
q = deque()
q.append(self)
while q:
node = q.popleft()
for child in node.child_items:
traversal.append(child)
q.append(child)
return traversal
def __str__(self) -> str:
return (
f"{self.item_code}{' - ' + self.name if self.is_bom else ''} qty(per unit): {self.qty}"
f" exploded_qty: {self.exploded_qty}"
)
def __repr__(self, level: int = 0) -> str:
rep = "" * (level - 1) + "┣━ " * (level > 0) + str(self) + "\n"
for child in self.child_items:
rep += child.__repr__(level=level + 1)
return rep
class BOM(WebsiteGenerator):
website = frappe._dict(
# page_title_field = "item_name",
@ -152,7 +224,7 @@ class BOM(WebsiteGenerator):
if not args:
args = frappe.form_dict.get('args')
if isinstance(args, string_types):
if isinstance(args, str):
import json
args = json.loads(args)
@ -600,6 +672,11 @@ class BOM(WebsiteGenerator):
if not d.batch_size or d.batch_size <= 0:
d.batch_size = 1
def get_tree_representation(self) -> BOMTree:
"""Get a complete tree representation preserving order of child items."""
return BOMTree(self.name)
def get_bom_item_rate(args, bom_doc):
if bom_doc.rm_cost_as_per == 'Valuation Rate':
rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)

View File

@ -2,14 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from collections import deque
import unittest
import frappe
from frappe.utils import cstr, flt
from frappe.test_runner import make_test_records
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
from six import string_types
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.tests.test_subcontracting import set_backflush_based_on
@ -227,11 +226,88 @@ class TestBOM(unittest.TestCase):
supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
self.assertEqual(bom_items, supplied_items)
def test_bom_tree_representation(self):
bom_tree = {
"Assembly": {
"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
"SubAssembly2": {"ChildPart3": {}},
"SubAssembly3": {"SubSubAssy1": {"ChildPart4": {}}},
"ChildPart5": {},
"ChildPart6": {},
"SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
}
}
parent_bom = create_nested_bom(bom_tree, prefix="")
created_tree = parent_bom.get_tree_representation()
reqd_order = level_order_traversal(bom_tree)[1:] # skip first item
created_order = created_tree.level_order_traversal()
self.assertEqual(len(reqd_order), len(created_order))
for reqd_item, created_item in zip(reqd_order, created_order):
self.assertEqual(reqd_item, created_item.item_code)
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
def level_order_traversal(node):
traversal = []
q = deque()
q.append(node)
while q:
node = q.popleft()
for node_name, subtree in node.items():
traversal.append(node_name)
q.append(subtree)
return traversal
def create_nested_bom(tree, prefix="_Test bom "):
""" Helper function to create a simple nested bom from tree describing item names. (along with required items)
"""
def create_items(bom_tree):
for item_code, subtree in bom_tree.items():
bom_item_code = prefix + item_code
if not frappe.db.exists("Item", bom_item_code):
frappe.get_doc(doctype="Item", item_code=bom_item_code, item_group="_Test Item Group").insert()
create_items(subtree)
create_items(tree)
def dfs(tree, node):
"""naive implementation for searching right subtree"""
for node_name, subtree in tree.items():
if node_name == node:
return subtree
else:
result = dfs(subtree, node)
if result is not None:
return result
order_of_creating_bom = reversed(level_order_traversal(tree))
for item in order_of_creating_bom:
child_items = dfs(tree, item)
if child_items:
bom_item_code = prefix + item
bom = frappe.get_doc(doctype="BOM", item=bom_item_code)
for child_item in child_items.keys():
bom.append("items", {"item_code": prefix + child_item})
bom.insert()
bom.submit()
return bom # parent bom is last bom
def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
if warehouse_list and isinstance(warehouse_list, string_types):
if warehouse_list and isinstance(warehouse_list, str):
warehouse_list = [warehouse_list]
if not warehouse_list:

View File

@ -389,17 +389,12 @@ class TestWorkOrder(unittest.TestCase):
ste.submit()
stock_entries.append(ste)
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name}, order_by='creation asc')
self.assertEqual(len(job_cards), len(bom.operations))
for i, job_card in enumerate(job_cards):
doc = frappe.get_doc("Job Card", job_card)
doc.append("time_logs", {
"from_time": add_to_date(None, i),
"hours": 1,
"to_time": add_to_date(None, i + 1),
"completed_qty": doc.for_quantity
})
doc.time_logs[0].completed_qty = 1
doc.submit()
ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))

View File

@ -1,7 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import json
import math
@ -30,9 +29,6 @@ class ItemHasVariantError(frappe.ValidationError): pass
class SerialNoQtyError(frappe.ValidationError):
pass
form_grid_templates = {
"operations": "templates/form_grid/work_order_grid.html"
}
class WorkOrder(Document):
def onload(self):
@ -472,46 +468,47 @@ class WorkOrder(Document):
def set_work_order_operations(self):
"""Fetch operations from BOM and set in 'Work Order'"""
self.set('operations', [])
def _get_operations(bom_no, qty=1):
return frappe.db.sql(
f"""select
operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins * {qty} as time_in_mins,
"Pending" as status, parent as bom, batch_size, sequence_id
from
`tabBOM Operation`
where
parent = %s order by idx
""", bom_no, as_dict=1)
self.set('operations', [])
if not self.bom_no:
return
if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
operations = []
if not self.use_multi_level_bom:
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
else:
bom_list = [self.bom_no]
bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
bom_traversal = list(reversed(bom_tree.level_order_traversal()))
bom_traversal.append(bom_tree) # add operation on top level item last
for d in bom_traversal:
if d.is_bom:
operations.extend(_get_operations(d.name, qty=d.exploded_qty))
for correct_index, operation in enumerate(operations, start=1):
operation.idx = correct_index
operations = frappe.db.sql("""
select
operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins,
"Pending" as status, parent as bom, batch_size, sequence_id
from
`tabBOM Operation`
where
parent in (%s) order by idx
""" % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
self.set('operations', operations)
if self.use_multi_level_bom and self.get('operations') and self.get('items'):
raw_material_operations = [d.operation for d in self.get('items')]
operations = [d.operation for d in self.get('operations')]
for operation in raw_material_operations:
if operation not in operations:
self.append('operations', {
'operation': operation
})
self.calculate_time()
def calculate_time(self):
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
for d in self.get("operations"):
d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * (flt(self.qty) / flt(d.batch_size))
d.time_in_mins = flt(d.time_in_mins) * (flt(self.qty) / flt(d.batch_size))
self.calculate_operating_cost()

View File

@ -2,7 +2,6 @@
"actions": [],
"creation": "2014-10-16 14:35:41.950175",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"details",
@ -49,6 +48,7 @@
{
"fieldname": "bom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "BOM",
"no_copy": 1,
"options": "BOM",
@ -68,6 +68,7 @@
"fieldtype": "Column Break"
},
{
"columns": 1,
"description": "Operation completed for how many finished goods?",
"fieldname": "completed_qty",
"fieldtype": "Float",
@ -77,6 +78,7 @@
"read_only": 1
},
{
"columns": 1,
"default": "Pending",
"fieldname": "status",
"fieldtype": "Select",
@ -119,6 +121,7 @@
"fieldtype": "Column Break"
},
{
"columns": 1,
"description": "in Minutes",
"fieldname": "time_in_mins",
"fieldtype": "Float",
@ -205,7 +208,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-01-12 14:48:31.061286",
"modified": "2021-06-24 14:36:12.835543",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",
@ -214,4 +217,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@ -286,7 +286,9 @@ erpnext.patches.v13_0.germany_fill_debtor_creditor_number
erpnext.patches.v13_0.set_pos_closing_as_failed
execute:frappe.rename_doc("Workspace", "Loan Management", "Loans", force=True)
erpnext.patches.v13_0.update_timesheet_changes
erpnext.patches.v13_0.add_doctype_to_sla #14-06-2021
erpnext.patches.v13_0.set_training_event_attendance
erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
erpnext.patches.v13_0.rename_issue_status_hold_to_on_hold
erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
erpnext.patches.v13_0.update_job_card_details

View File

@ -0,0 +1,21 @@
# Copyright (c) 2020, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.reload_doc('support', 'doctype', 'sla_fulfilled_on_status')
frappe.reload_doc('support', 'doctype', 'service_level_agreement')
if frappe.db.has_column('Service Level Agreement', 'enable'):
rename_field('Service Level Agreement', 'enable', 'enabled')
for sla in frappe.get_all('Service Level Agreement'):
agreement = frappe.get_doc('Service Level Agreement', sla.name)
agreement.document_type = 'Issue'
agreement.apply_sla_for_resolution = 1
agreement.append('sla_fulfilled_on', {'status': 'Resolved'})
agreement.append('sla_fulfilled_on', {'status': 'Closed'})
agreement.save()

View File

@ -5,4 +5,4 @@ def execute():
frappe.reload_doctype("Buying Settings")
buying_settings = frappe.get_single("Buying Settings")
buying_settings.bill_for_rejected_quantity_in_purchase_invoice = 0
buying_settings.save()
buying_settings.save()

View File

@ -135,10 +135,26 @@ frappe.ui.form.on('Payroll Entry', {
});
frm.set_query('employee', 'employees', () => {
if (!frm.doc.company) {
frappe.msgprint(__("Please set a Company"));
return [];
let error_fields = [];
let mandatory_fields = ['company', 'payroll_frequency', 'start_date', 'end_date'];
let message = __('Mandatory fields required in {0}', [__(frm.doc.doctype)]);
mandatory_fields.forEach(field => {
if (!frm.doc[field]) {
error_fields.push(frappe.unscrub(field));
}
});
if (error_fields && error_fields.length) {
message = message + '<br><br><ul><li>' + error_fields.join('</li><li>') + "</ul>";
frappe.throw({
message: message,
indicator: 'red',
title: __('Missing Fields')
});
}
return {
query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query",
filters: frm.events.get_employee_filters(frm)
@ -148,25 +164,22 @@ frappe.ui.form.on('Payroll Entry', {
get_employee_filters: function (frm) {
let filters = {};
filters['company'] = frm.doc.company;
filters['start_date'] = frm.doc.start_date;
filters['end_date'] = frm.doc.end_date;
filters['salary_slip_based_on_timesheet'] = frm.doc.salary_slip_based_on_timesheet;
filters['payroll_frequency'] = frm.doc.payroll_frequency;
filters['payroll_payable_account'] = frm.doc.payroll_payable_account;
filters['currency'] = frm.doc.currency;
if (frm.doc.department) {
filters['department'] = frm.doc.department;
}
if (frm.doc.branch) {
filters['branch'] = frm.doc.branch;
}
if (frm.doc.designation) {
filters['designation'] = frm.doc.designation;
}
let fields = ['company', 'start_date', 'end_date', 'payroll_frequency', 'payroll_payable_account',
'currency', 'department', 'branch', 'designation'];
fields.forEach(field => {
if (frm.doc[field]) {
filters[field] = frm.doc[field];
}
});
if (frm.doc.employees) {
filters['employees'] = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
let employees = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
if (employees && employees.length) {
filters['employees'] = employees;
}
}
return filters;
},

View File

@ -459,6 +459,7 @@ def get_emp_list(sal_struct, cond, end_date, payroll_payable_account):
where
t1.name = t2.employee
and t2.docstatus = 1
and t1.status != 'Inactive'
%s order by t2.from_date desc
""" % cond, {"sal_struct": tuple(sal_struct), "from_date": end_date, "payroll_payable_account": payroll_payable_account}, as_dict=True)
@ -679,6 +680,10 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
conditions = []
include_employees = []
emp_cond = ''
if not filters.payroll_frequency:
frappe.throw(_('Select Payroll Frequency.'))
if filters.start_date and filters.end_date:
employee_list = get_employee_list(filters)
emp = filters.get('employees')

View File

@ -667,16 +667,13 @@ class SalarySlip(TransactionBase):
component_row = self.append(component_type)
for attr in (
'depends_on_payment_days', 'salary_component',
'depends_on_payment_days', 'salary_component', 'abbr',
'do_not_include_in_total', 'is_tax_applicable',
'is_flexible_benefit', 'variable_based_on_taxable_salary',
'exempted_from_income_tax'
):
component_row.set(attr, component_data.get(attr))
abbr = component_data.get('abbr') or component_data.get('salary_component_abbr')
component_row.set('abbr', abbr)
if additional_salary:
component_row.default_amount = 0
component_row.additional_amount = amount

View File

@ -6,12 +6,12 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import set_request
from frappe.website.render import render
from frappe.website.serve import get_response
class TestHomepage(unittest.TestCase):
def test_homepage_load(self):
set_request(method='GET', path='home')
response = render()
response = get_response()
self.assertEqual(response.status_code, 200)

View File

@ -7,7 +7,7 @@ import frappe
import unittest
from bs4 import BeautifulSoup
from frappe.utils import set_request
from frappe.website.render import render
from frappe.website.serve import get_response
class TestHomepageSection(unittest.TestCase):
def test_homepage_section_card(self):
@ -26,7 +26,7 @@ class TestHomepageSection(unittest.TestCase):
pass
set_request(method='GET', path='home')
response = render()
response = get_response()
self.assertEqual(response.status_code, 200)
@ -59,7 +59,7 @@ class TestHomepageSection(unittest.TestCase):
}).insert()
set_request(method='GET', path='home')
response = render()
response = get_response()
self.assertEqual(response.status_code, 200)

View File

@ -2,10 +2,8 @@ from __future__ import unicode_literals
from bs4 import BeautifulSoup
import frappe, unittest
from frappe.utils import set_request, get_html_for_route
from frappe.website.render import render
from frappe.utils import get_html_for_route
from erpnext.portal.product_configurator.utils import get_products_for_website
from erpnext.stock.doctype.item.test_item import make_item_variant
test_dependencies = ["Item"]

View File

@ -1,29 +0,0 @@
.panel-header {
background-color: #fafbfc;
border: 1px solid #d1d8dd;
border-radius: 3px 3px 0 0;
}
.panel-body {
background-color: #fff;
border: 1px solid #d1d8dd;
border-top: none;
border-radius: 0 0 3px 3px;
overflow-wrap: break-word;
}
.sender-avatar {
width: 24px;
height: 24px;
border-radius: 3px;
vertical-align: middle;
}
.sender-avatar-placeholder {
width: 24px;
height: 24px;
border-radius: 3px;
vertical-align: middle;
line-height: 24px;
text-align: center;
color: #d1d8dd;
border: 1px solid #d1d8dd;
background-color: #fff;
}

View File

@ -1,408 +0,0 @@
.erpnext-footer {
margin: 11px auto;
text-align: center;
}
.show-all-reports {
margin-top: 5px;
font-size: 11px;
}
/* toolbar */
.toolbar-splash {
width: 32px;
height: 32px;
margin: -10px auto;
}
.erpnext-icon {
width: 24px;
margin-right: 0px;
margin-top: -3px;
}
.dashboard-list-item {
background-color: inherit;
padding: 5px 0px;
border-bottom: 1px solid #d1d8dd;
}
#page-stock-balance .dashboard-list-item {
padding: 5px 15px;
}
.dashboard-list-item:last-child {
border-bottom: none;
}
.frappe-control[data-fieldname='result_html'] {
overflow: scroll;
}
.assessment-result-tool {
table-layout: fixed;
}
.assessment-result-tool input {
width: 100%;
border: 0;
outline: none;
text-align: right;
}
.assessment-result-tool th {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.assessment-result-tool .total-score,
.assessment-result-tool .grade,
.assessment-result-tool .score {
text-align: right;
}
/* pos */
body[data-route="pos"] .pos-bill-toolbar {
padding: 10px 0px;
height: 51px;
}
body[data-route="pos"] .pos-bill-item:hover,
body[data-route="pos"] .list-customers-table > .pos-list-row:hover {
background-color: #f5f7fa;
cursor: pointer;
}
body[data-route="pos"] .pos-item-qty {
display: inline-block;
}
body[data-route="pos"] .pos-qty-row > div {
padding: 5px 0px;
}
body[data-route="pos"] .pos-qty-btn {
margin-top: 3px;
cursor: pointer;
font-size: 120%;
}
body[data-route="pos"] .search-area .form-group {
max-width: 100% !important;
}
body[data-route="pos"] .tax-table {
margin-bottom: 10px;
}
body[data-route="pos"] .discount-field-col {
padding-left: 24px;
}
body[data-route="pos"] .discount-amount-area .input-group:first-child {
margin-bottom: 2px;
}
body[data-route="pos"] .payment-toolbar .row {
width: 323px;
margin: 0 auto;
}
body[data-route="pos"] .payment-mode {
cursor: pointer;
font-family: sans-serif;
font-size: 15px;
}
body[data-route="pos"] .pos-payment-row .col-xs-6 {
padding: 15px;
}
body[data-route="pos"] .pos-payment-row {
border-bottom: 1px solid #d1d8dd;
margin: 2px 0px 5px 0px;
height: 60px;
margin-top: 0px;
margin-bottom: 0px;
}
body[data-route="pos"] .pos-payment-row:hover,
body[data-route="pos"] .pos-keyboard-key:hover {
background-color: #fafbfc;
cursor: pointer;
}
body[data-route="pos"] .pos-keyboard-key,
body[data-route="pos"] .delete-btn {
border: 1px solid #d1d8dd;
height: 85px;
width: 85px;
margin: 10px 10px;
font-size: 24px;
font-weight: 200;
background-color: #FDFDFD;
border-color: #e8e8e8;
}
body[data-route="pos"] .numeric-keypad {
border: 1px solid #d1d8dd;
height: 69px;
width: 69px;
font-size: 20px;
font-weight: 200;
background-color: #FDFDFD;
border-color: #e8e8e8;
margin-left: -4px;
}
body[data-route="pos"] .pos-pay {
height: 69px;
width: 69px;
font-size: 17px;
font-weight: 200;
margin-left: -4px;
}
body[data-route="pos"] .numeric-keypad {
height: 60px;
width: 60px;
font-size: 20px;
font-weight: 200;
border-radius: 0;
background-color: #fff;
margin-left: -4px;
}
@media (max-width: 1199px) {
body[data-route="pos"] .numeric-keypad {
height: 45px;
width: 45px;
font-size: 14px;
}
}
@media (max-width: 991px) {
body[data-route="pos"] .numeric-keypad {
height: 40px;
width: 40px;
}
}
body[data-route="pos"] .numeric_keypad {
margin-left: -15px;
}
body[data-route="pos"] .numeric_keypad > .row > button {
border: none;
border-right: 1px solid #d1d8dd;
border-bottom: 1px solid #d1d8dd;
}
body[data-route="pos"] .numeric_keypad > .row > button:first-child {
border-left: 1px solid #d1d8dd;
}
body[data-route="pos"] .numeric_keypad > .row:first-child > button {
border-top: 1px solid #d1d8dd;
}
body[data-route="pos"] .pos-pay {
background-color: #5E64FF;
border: none;
}
body[data-route="pos"] .multimode-payments {
padding-left: 30px;
}
body[data-route="pos"] .payment-toolbar {
padding-right: 30px;
}
body[data-route="pos"] .list-row-head.pos-invoice-list {
border-top: 1px solid #d1d8dd;
}
body[data-route="pos"] .modal-dialog {
width: 750px;
}
@media (max-width: 767px) {
body[data-route="pos"] .modal-dialog {
width: auto;
}
body[data-route="pos"] .modal-dialog .modal-content {
height: auto;
}
}
@media (max-width: 767px) {
body[data-route="pos"] .amount-row h3 {
font-size: 15px;
}
body[data-route="pos"] .pos-keyboard-key,
body[data-route="pos"] .delete-btn {
height: 50px;
}
body[data-route="pos"] .multimode-payments {
padding-left: 15px;
}
body[data-route="pos"] .payment-toolbar {
padding-right: 15px;
}
}
body[data-route="pos"] .amount-label {
font-size: 16px;
}
body[data-route="pos"] .selected-payment-mode {
background-color: #fafbfc;
cursor: pointer;
}
body[data-route="pos"] .pos-invoice-list {
padding: 15px 10px;
}
body[data-route="pos"] .write_off_amount,
body[data-route="pos"] .change_amount {
margin: 15px;
width: 130px;
}
body[data-route="pos"] .pos-list-row {
display: table;
table-layout: fixed;
width: 100%;
padding: 9px 15px;
font-size: 12px;
margin: 0px;
border-bottom: 1px solid #d1d8dd;
}
body[data-route="pos"] .pos-list-row .cell {
display: table-cell;
vertical-align: middle;
}
body[data-route="pos"] .pos-list-row .cell.price-cell {
width: 50%;
}
body[data-route="pos"] .pos-list-row .subject {
width: 40%;
}
body[data-route="pos"] .pos-list-row .list-row-checkbox,
body[data-route="pos"] .pos-list-row .list-select-all {
margin-right: 7px;
}
body[data-route="pos"] .pos-bill-header {
background-color: #f5f7fa;
border: 1px solid #d1d8dd;
padding: 13px 15px;
}
body[data-route="pos"] .pos-list-row.active {
background-color: #fffce7;
}
body[data-route="pos"] .totals-area {
border-right: 1px solid #d1d8dd;
border-left: 1px solid #d1d8dd;
margin-bottom: 15px;
}
body[data-route="pos"] .tax-area .pos-list-row {
border: none;
}
body[data-route="pos"] .item-cart-items {
height: calc(100vh - 526px);
overflow: auto;
border: 1px solid #d1d8dd;
border-top: none;
}
@media (max-width: 767px) {
body[data-route="pos"] .item-cart-items {
height: 30vh;
}
}
body[data-route="pos"] .no-items-message {
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
body[data-route="pos"] .pos-list-row:last-child {
border-bottom: none;
}
body[data-route="pos"] .form-section-heading {
padding: 0;
}
body[data-route="pos"] .item-list {
border: 1px solid #d1d8dd;
border-top: none;
max-height: calc(100vh - 190px);
overflow: auto;
}
@media (max-width: 767px) {
body[data-route="pos"] .item-list {
max-height: initial;
}
}
body[data-route="pos"] .item-list .image-field {
height: 140px;
}
body[data-route="pos"] .item-list .image-field .placeholder-text {
font-size: 50px;
}
body[data-route="pos"] .item-list .pos-item-wrapper {
position: relative;
}
body[data-route="pos"] .pos-bill-toolbar {
margin-top: 10px;
}
body[data-route="pos"] .search-item .form-group {
margin: 0;
}
body[data-route="pos"] .item-list-area .pos-bill-header {
padding: 5px;
padding-left: 15px;
}
body[data-route="pos"] .pos-selected-item-action .pos-list-row:first-child {
padding-top: 0;
}
body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
border: none;
}
@media (max-width: 1199px) {
body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
padding: 5px 15px;
}
}
body[data-route="pos"] .edit-customer-btn {
position: absolute;
right: 57px;
top: 15px;
z-index: 100;
}
body[data-route="pos"] .btn-more {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background-color: #fafbfc;
min-height: 200px;
}
body[data-route="pos"] .collapse-btn {
cursor: pointer;
}
@media (max-width: 767px) {
body[data-route="pos"] .page-actions {
max-width: 110px;
}
}
.price-info {
position: absolute;
left: 0;
bottom: 0;
margin: 0 0 15px 15px;
background-color: rgba(141, 153, 166, 0.6);
padding: 5px 9px;
border-radius: 3px;
color: #fff;
}
.leaderboard .result {
border-top: 1px solid #d1d8dd;
}
.leaderboard .list-item {
padding-left: 45px;
}
.leaderboard .list-item_content {
padding-right: 45px;
}
.exercise-card {
box-shadow: 0 1px 3px rgba(0,0,0,0.30);
border-radius: 2px;
padding: 6px 6px 6px 8px;
margin-top: 10px;
height: 100% !important;
}
.exercise-card .card-img-top {
width: 100%;
height: 15vw;
object-fit: cover;
}
.exercise-card .btn-edit {
position: absolute;
bottom: 10px;
left: 20px;
}
.exercise-card .btn-del {
position: absolute;
bottom: 10px;
left: 50px;
}
.exercise-card .card-body {
margin-bottom: 10px;
}
.exercise-card .card-footer {
padding: 10px;
}
.exercise-row {
height: 100% !important;
display: flex;
flex-wrap: wrap;
}
.exercise-col {
padding: 10px;
}

View File

@ -1,611 +0,0 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-map-pane svg,
.leaflet-map-pane canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer {
max-width: none !important;
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane {
z-index: 400;
}
.leaflet-tile-pane {
z-index: 200;
}
.leaflet-overlay-pane {
z-index: 400;
}
.leaflet-shadow-pane {
z-index: 500;
}
.leaflet-marker-pane {
z-index: 600;
}
.leaflet-popup-pane {
z-index: 700;
}
.leaflet-map-pane canvas {
z-index: 100;
}
.leaflet-map-pane svg {
z-index: 200;
}
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
-o-transition: -o-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
transition: transform 0.25s cubic-bezier(0, 0, 0.25, 1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255, 255, 255, 0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-control-zoom-out {
font-size: 20px;
}
.leaflet-touch .leaflet-control-zoom-in {
font-size: 22px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 24px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url('assets/erpnext/images/leaflet/layers.png');
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url('assets/erpnext/images/leaflet/layers-2x.png');
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0, 0, 0, 0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid: DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}

View File

@ -1,316 +0,0 @@
/* ================================================================== */
/* Toolbars
/* ================================================================== */
.leaflet-draw-section {
position: relative;
}
.leaflet-draw-toolbar {
margin-top: 12px;
}
.leaflet-draw-toolbar-top {
margin-top: 0;
}
.leaflet-draw-toolbar-notop a:first-child {
border-top-right-radius: 0;
}
.leaflet-draw-toolbar-nobottom a:last-child {
border-bottom-right-radius: 0;
}
.leaflet-draw-toolbar a {
background-image: url('assets/erpnext/images/leaflet/spritesheet.png');
background-repeat: no-repeat;
}
.leaflet-retina .leaflet-draw-toolbar a {
background-image: url('assets/erpnext/images/leaflet/spritesheet-2x.png');
background-size: 270px 30px;
}
.leaflet-draw a {
display: block;
text-align: center;
text-decoration: none;
}
/* ================================================================== */
/* Toolbar actions menu
/* ================================================================== */
.leaflet-draw-actions {
display: none;
list-style: none;
margin: 0;
padding: 0;
position: absolute;
left: 26px;
/* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
top: 0;
white-space: nowrap;
}
.leaflet-right .leaflet-draw-actions {
right: 26px;
left: auto;
}
.leaflet-draw-actions li {
display: inline-block;
}
.leaflet-draw-actions li:first-child a {
border-left: none;
}
.leaflet-draw-actions li:last-child a {
-webkit-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.leaflet-right .leaflet-draw-actions li:last-child a {
-webkit-border-radius: 0;
border-radius: 0;
}
.leaflet-right .leaflet-draw-actions li:first-child a {
-webkit-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.leaflet-draw-actions a {
background-color: #919187;
border-left: 1px solid #AAA;
color: #FFF;
font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
line-height: 28px;
text-decoration: none;
padding-left: 10px;
padding-right: 10px;
height: 28px;
}
.leaflet-draw-actions-bottom {
margin-top: 0;
}
.leaflet-draw-actions-top {
margin-top: 1px;
}
.leaflet-draw-actions-top a,
.leaflet-draw-actions-bottom a {
height: 27px;
line-height: 27px;
}
.leaflet-draw-actions a:hover {
background-color: #A0A098;
}
.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
height: 26px;
line-height: 26px;
}
/* ================================================================== */
/* Draw toolbar
/* ================================================================== */
.leaflet-draw-toolbar .leaflet-draw-draw-polyline {
background-position: -2px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-polygon {
background-position: -31px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
background-position: -62px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-circle {
background-position: -92px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-marker {
background-position: -122px -2px;
}
/* ================================================================== */
/* Edit toolbar
/* ================================================================== */
.leaflet-draw-toolbar .leaflet-draw-edit-edit {
background-position: -152px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-remove {
background-position: -182px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
background-position: -212px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
background-position: -242px -2px;
}
/* ================================================================== */
/* Drawing styles
/* ================================================================== */
.leaflet-mouse-marker {
background-color: #fff;
cursor: crosshair;
}
.leaflet-draw-tooltip {
background: rgb(54, 54, 54);
background: rgba(0, 0, 0, 0.5);
border: 1px solid transparent;
-webkit-border-radius: 4px;
border-radius: 4px;
color: #fff;
font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
margin-left: 20px;
margin-top: -21px;
padding: 4px 8px;
position: absolute;
visibility: hidden;
white-space: nowrap;
z-index: 6;
}
.leaflet-draw-tooltip:before {
border-right: 6px solid black;
border-right-color: rgba(0, 0, 0, 0.5);
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
content: "";
position: absolute;
top: 7px;
left: -7px;
}
.leaflet-error-draw-tooltip {
background-color: #F2DEDE;
border: 1px solid #E6B6BD;
color: #B94A48;
}
.leaflet-error-draw-tooltip:before {
border-right-color: #E6B6BD;
}
.leaflet-draw-tooltip-single {
margin-top: -12px
}
.leaflet-draw-tooltip-subtext {
color: #f8d5e4;
}
.leaflet-draw-guide-dash {
font-size: 1%;
opacity: 0.6;
position: absolute;
width: 5px;
height: 5px;
}
/* ================================================================== */
/* Edit styles
/* ================================================================== */
.leaflet-edit-marker-selected {
background: rgba(254, 87, 161, 0.1);
border: 4px dashed rgba(254, 87, 161, 0.6);
-webkit-border-radius: 4px;
border-radius: 4px;
}
.leaflet-edit-move {
cursor: move;
}
.leaflet-edit-resize {
cursor: pointer;
}
/* ================================================================== */
/* Old IE styles
/* ================================================================== */
.leaflet-oldie .leaflet-draw-toolbar {
border: 3px solid #999;
}
.leaflet-oldie .leaflet-draw-toolbar a {
background-color: #eee;
}
.leaflet-oldie .leaflet-draw-toolbar a:hover {
background-color: #fff;
}
.leaflet-oldie .leaflet-draw-actions {
left: 32px;
margin-top: 3px;
}
.leaflet-oldie .leaflet-draw-actions li {
display: inline;
zoom: 1;
}
.leaflet-oldie .leaflet-edit-marker-selected {
border: 4px dashed #fe93c2;
}
.leaflet-oldie .leaflet-draw-actions a {
background-color: #999;
}
.leaflet-oldie .leaflet-draw-actions a:hover {
background-color: #a5a5a5;
}
.leaflet-oldie .leaflet-draw-actions-top a {
margin-top: 1px;
}
.leaflet-oldie .leaflet-draw-actions-bottom a {
height: 28px;
line-height: 28px;
}
.leaflet-oldie .leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
height: 27px;
line-height: 27px;
}

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
init: function(wrapper, title) {
this._super({
erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridReport {
constructor(wrapper, title) {
super({
title: title,
parent: $(wrapper).find('.layout-main'),
page: wrapper.page,
@ -33,8 +33,24 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
}
},
});
},
setup_columns: function() {
this.filters = [
{fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company",
default_value: __("Select Company..."),
filter: function(val, item, opts, me) {
if (item.company == val || val == opts.default_value) {
return me.apply_zero_filter(val, item, opts, me);
}
return false;
}},
{fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year",
default_value: __("Select Fiscal Year...")},
{fieldtype: "Date", label: __("From Date"), fieldname: "from_date"},
{fieldtype: "Label", label: __("To")},
{fieldtype: "Date", label: __("To Date"), fieldname: "to_date"}
]
}
setup_columns() {
this.columns = [
{id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title"},
{id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100,
@ -50,25 +66,10 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
formatter: this.currency_formatter}
];
}
},
filters: [
{fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company",
default_value: __("Select Company..."),
filter: function(val, item, opts, me) {
if (item.company == val || val == opts.default_value) {
return me.apply_zero_filter(val, item, opts, me);
}
return false;
}},
{fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year",
default_value: __("Select Fiscal Year...")},
{fieldtype: "Date", label: __("From Date"), fieldname: "from_date"},
{fieldtype: "Label", label: __("To")},
{fieldtype: "Date", label: __("To Date"), fieldname: "to_date"}
],
setup_filters: function() {
this._super();
setup_filters() {
super.setup_filters();
var me = this;
// default filters
this.filter_inputs.fiscal_year.change(function() {
@ -83,8 +84,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
});
me.show_zero_check()
if(me.ignore_closing_entry) me.ignore_closing_entry();
},
prepare_data: function() {
}
prepare_data() {
var me = this;
if(!this.primary_data) {
// make accounts list
@ -113,12 +114,12 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
this.set_indent();
this.prepare_balances();
},
init_account: function(d) {
}
init_account(d) {
this.reset_item_values(d);
},
}
prepare_balances: function() {
prepare_balances() {
var gl = frappe.report_dump.data['GL Entry'];
var me = this;
@ -139,8 +140,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
});
this.update_groups();
},
update_balances: function(account, posting_date, v) {
}
update_balances(account, posting_date, v) {
// opening
if (posting_date < this.opening_date || v.is_opening === "Yes") {
if (account.report_type === "Profit and Loss" &&
@ -161,8 +162,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
var closing_bal = flt(account.opening_dr) - flt(account.opening_cr) +
flt(account.debit) - flt(account.credit);
this.set_debit_or_credit(account, "closing", closing_bal);
},
set_debit_or_credit: function(account, field, balance) {
}
set_debit_or_credit(account, field, balance) {
if(balance > 0) {
account[field+"_dr"] = balance;
account[field+"_cr"] = 0;
@ -170,8 +171,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
account[field+"_cr"] = Math.abs(balance);
account[field+"_dr"] = 0;
}
},
update_groups: function() {
}
update_groups() {
// update groups
var me= this;
$.each(this.data, function(i, account) {
@ -202,9 +203,9 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
}
}
});
},
}
set_fiscal_year: function() {
set_fiscal_year() {
if (this.opening_date > this.closing_date) {
frappe.msgprint(__("Opening Date should be before Closing Date"));
return;
@ -223,9 +224,9 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
frappe.msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
return;
}
},
}
show_general_ledger: function(account) {
show_general_ledger(account) {
frappe.route_options = {
account: account,
company: this.company,
@ -234,4 +235,4 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
};
frappe.set_route("query-report", "General Ledger");
}
});
};

View File

@ -0,0 +1,3 @@
import "./bank_reconciliation_tool/data_table_manager";
import "./bank_reconciliation_tool/number_card";
import "./bank_reconciliation_tool/dialog_manager";

View File

@ -9,14 +9,14 @@ cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
cur_frm.email_field = "contact_email";
erpnext.buying.BuyingController = erpnext.TransactionController.extend({
setup: function() {
this._super();
},
erpnext.buying.BuyingController = class BuyingController extends erpnext.TransactionController {
setup() {
super.setup();
}
onload: function(doc, cdt, cdn) {
onload(doc, cdt, cdn) {
this.setup_queries(doc, cdt, cdn);
this._super();
super.onload();
this.frm.set_query('shipping_rule', function() {
return {
@ -48,9 +48,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
});
}
/* eslint-enable */
},
}
setup_queries: function(doc, cdt, cdn) {
setup_queries(doc, cdt, cdn) {
var me = this;
if(this.frm.fields_dict.buying_price_list) {
@ -109,9 +109,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
return me.set_query_for_item_tax_template(doc, cdt, cdn)
});
}
},
}
refresh: function(doc) {
refresh(doc) {
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier'};
this.frm.toggle_display("supplier_name",
@ -123,10 +123,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
this.toggle_subcontracting_fields();
this._super();
},
super.refresh();
}
toggle_subcontracting_fields: function() {
toggle_subcontracting_fields() {
if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) {
this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty',
'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM');
@ -134,37 +134,37 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
this.frm.set_df_property('supplied_items', 'cannot_add_rows', 1);
this.frm.set_df_property('supplied_items', 'cannot_delete_rows', 1);
}
},
}
supplier: function() {
supplier() {
var me = this;
erpnext.utils.get_party_details(this.frm, null, null, function(){
me.apply_price_list();
});
},
}
supplier_address: function() {
supplier_address() {
erpnext.utils.get_address_display(this.frm);
erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address");
},
}
buying_price_list: function() {
buying_price_list() {
this.apply_price_list();
},
}
discount_percentage: function(doc, cdt, cdn) {
discount_percentage(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
item.discount_amount = 0.0;
this.price_list_rate(doc, cdt, cdn);
},
}
discount_amount: function(doc, cdt, cdn) {
discount_amount(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
item.discount_percentage = 0.0;
this.price_list_rate(doc, cdt, cdn);
},
}
qty: function(doc, cdt, cdn) {
qty(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
@ -179,22 +179,22 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
}
this._super(doc, cdt, cdn);
},
super.qty(doc, cdt, cdn);
}
batch_no: function(doc, cdt, cdn) {
this._super(doc, cdt, cdn);
},
batch_no(doc, cdt, cdn) {
super.batch_no(doc, cdt, cdn);
}
received_qty: function(doc, cdt, cdn) {
received_qty(doc, cdt, cdn) {
this.calculate_accepted_qty(doc, cdt, cdn)
},
}
rejected_qty: function(doc, cdt, cdn) {
rejected_qty(doc, cdt, cdn) {
this.calculate_accepted_qty(doc, cdt, cdn)
},
}
calculate_accepted_qty: function(doc, cdt, cdn){
calculate_accepted_qty(doc, cdt, cdn){
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
@ -202,9 +202,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
this.qty(doc, cdt, cdn);
},
}
validate_negative_quantity: function(cdt, cdn, item, fieldnames){
validate_negative_quantity(cdt, cdn, item, fieldnames){
if(!item || !fieldnames) { return }
var is_negative_qty = false;
@ -217,9 +217,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
return is_negative_qty
},
}
warehouse: function(doc, cdt, cdn) {
warehouse(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(item.item_code && item.warehouse) {
return this.frm.call({
@ -232,9 +232,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
});
}
},
}
project: function(doc, cdt, cdn) {
project(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(item.project) {
$.each(this.frm.doc["items"] || [],
@ -245,48 +245,48 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
});
}
},
}
rejected_warehouse: function(doc, cdt) {
rejected_warehouse(doc, cdt) {
// trigger autofill_warehouse only if parent rejected_warehouse field is triggered
if (["Purchase Invoice", "Purchase Receipt"].includes(cdt)) {
this.autofill_warehouse(doc.items, "rejected_warehouse", doc.rejected_warehouse);
}
},
}
category: function(doc, cdt, cdn) {
category(doc, cdt, cdn) {
// should be the category field of tax table
if(cdt != doc.doctype) {
this.calculate_taxes_and_totals();
}
},
add_deduct_tax: function(doc, cdt, cdn) {
}
add_deduct_tax(doc, cdt, cdn) {
this.calculate_taxes_and_totals();
},
}
set_from_product_bundle: function() {
set_from_product_bundle() {
var me = this;
this.frm.add_custom_button(__("Product Bundle"), function() {
erpnext.buying.get_items_from_product_bundle(me.frm);
}, __("Get Items From"));
},
}
shipping_address: function(){
shipping_address(){
var me = this;
erpnext.utils.get_address_display(this.frm, "shipping_address",
"shipping_address_display", true);
},
}
billing_address: function() {
billing_address() {
erpnext.utils.get_address_display(this.frm, "billing_address",
"billing_address_display", true);
},
}
tc_name: function() {
tc_name() {
this.get_terms();
},
}
update_auto_repeat_reference: function(doc) {
update_auto_repeat_reference(doc) {
if (doc.auto_repeat) {
frappe.call({
method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
@ -303,9 +303,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
})
}
},
}
manufacturer: function(doc, cdt, cdn) {
manufacturer(doc, cdt, cdn) {
const row = locals[cdt][cdn];
if(row.manufacturer) {
@ -322,9 +322,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
});
}
},
}
manufacturer_part_no: function(doc, cdt, cdn) {
manufacturer_part_no(doc, cdt, cdn) {
const row = locals[cdt][cdn];
if (row.manufacturer_part_no) {
@ -347,7 +347,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}
}
});
};
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
@ -508,4 +508,4 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
});
dialog.show();
}
}

View File

@ -3,22 +3,22 @@
frappe.provide("erpnext.stock");
erpnext.stock.StockController = frappe.ui.form.Controller.extend({
onload: function() {
erpnext.stock.StockController = class StockController extends frappe.ui.form.Controller {
onload() {
// warehouse query if company
if (this.frm.fields_dict.company) {
this.setup_warehouse_query();
}
},
}
setup_warehouse_query: function() {
setup_warehouse_query() {
var me = this;
erpnext.queries.setup_queries(this.frm, "Warehouse", function() {
return erpnext.queries.warehouse(me.frm.doc);
});
},
}
setup_posting_date_time_check: function() {
setup_posting_date_time_check() {
// make posting date default and read only unless explictly checked
frappe.ui.form.on(this.frm.doctype, 'set_posting_date_and_time_read_only', function(frm) {
if(frm.doc.docstatus == 0 && frm.doc.set_posting_time) {
@ -46,9 +46,9 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
frm.trigger('set_posting_date_and_time_read_only');
}
});
},
}
show_stock_ledger: function() {
show_stock_ledger() {
var me = this;
if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__("Stock Ledger"), function() {
@ -63,9 +63,9 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
}, __("View"));
}
},
}
show_general_ledger: function() {
show_general_ledger() {
var me = this;
if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
@ -81,4 +81,4 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
}, __("View"));
}
}
});
};

View File

@ -1,12 +1,12 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.taxes_and_totals = erpnext.payments.extend({
setup: function() {
erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
setup() {
this.fetch_round_off_accounts();
},
}
apply_pricing_rule_on_item: function(item) {
apply_pricing_rule_on_item(item) {
let effective_item_rate = item.price_list_rate;
let item_rate = item.rate;
if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
@ -32,9 +32,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
},
}
calculate_taxes_and_totals: function(update_paid_amount) {
calculate_taxes_and_totals(update_paid_amount) {
this.discount_amount_applied = false;
this._calculate_taxes_and_totals();
this.calculate_discount_amount();
@ -63,16 +63,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
this.frm.refresh_fields();
},
}
calculate_discount_amount: function(){
calculate_discount_amount(){
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
this.set_discount_amount();
this.apply_discount_amount();
}
},
}
_calculate_taxes_and_totals: function() {
_calculate_taxes_and_totals() {
frappe.run_serially([
() => this.validate_conversion_rate(),
() => this.calculate_item_values(),
@ -85,9 +85,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
() => this.calculate_totals(),
() => this._cleanup()
]);
},
}
validate_conversion_rate: function() {
validate_conversion_rate() {
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, (cur_frm) ? precision("conversion_rate") : 9);
var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate",
this.frm.doc.name);
@ -102,9 +102,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
frappe.throw(err_message);
}
}
},
}
calculate_item_values: function() {
calculate_item_values() {
var me = this;
if (!this.discount_amount_applied) {
$.each(this.frm.doc["items"] || [], function(i, item) {
@ -124,16 +124,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
me.set_in_company_currency(item, ["price_list_rate", "rate", "amount", "net_rate", "net_amount"]);
});
}
},
}
set_in_company_currency: function(doc, fields) {
set_in_company_currency(doc, fields) {
var me = this;
$.each(fields, function(i, f) {
doc["base_"+f] = flt(flt(doc[f], precision(f, doc)) * me.frm.doc.conversion_rate, precision("base_" + f, doc));
});
},
}
initialize_taxes: function() {
initialize_taxes() {
var me = this;
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
@ -155,9 +155,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
frappe.model.round_floats_in(tax);
});
},
}
fetch_round_off_accounts: function() {
fetch_round_off_accounts() {
let me = this;
frappe.flags.round_off_applicable_accounts = [];
@ -168,14 +168,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
"company": me.frm.doc.company,
"account_list": frappe.flags.round_off_applicable_accounts
},
callback: function(r) {
frappe.flags.round_off_applicable_accounts.push(...r.message);
callback(r) {
if (r.message) {
frappe.flags.round_off_applicable_accounts.push(...r.message);
}
}
});
}
},
}
determine_exclusive_rate: function() {
determine_exclusive_rate() {
var me = this;
var has_inclusive_tax = false;
@ -213,9 +215,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
}
});
},
}
get_current_tax_fraction: function(tax, item_tax_map) {
get_current_tax_fraction(tax, item_tax_map) {
// Get tax fraction for calculating tax exclusive amount
// from tax inclusive amount
var current_tax_fraction = 0.0;
@ -244,14 +246,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
inclusive_tax_amount_per_qty *= -1;
}
return [current_tax_fraction, inclusive_tax_amount_per_qty];
},
}
_get_tax_rate: function(tax, item_tax_map) {
_get_tax_rate(tax, item_tax_map) {
return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ?
flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate;
},
}
calculate_net_total: function() {
calculate_net_total() {
var me = this;
this.frm.doc.total_qty = this.frm.doc.total = this.frm.doc.base_total = this.frm.doc.net_total = this.frm.doc.base_net_total = 0.0;
@ -264,9 +266,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
});
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
},
}
update_item_tax_map: function() {
update_item_tax_map() {
let me = this;
let item_codes = [];
let item_rates = {};
@ -304,9 +306,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
});
}
},
}
add_taxes_from_item_tax_template: function(item_tax_map) {
add_taxes_from_item_tax_template(item_tax_map) {
let me = this;
if (item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
@ -324,9 +326,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
});
}
},
}
calculate_taxes: function() {
calculate_taxes() {
var me = this;
this.frm.doc.rounding_adjustment = 0;
var actual_tax_dict = {};
@ -405,9 +407,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
});
});
},
}
set_cumulative_total: function(row_idx, tax) {
set_cumulative_total(row_idx, tax) {
var tax_amount = tax.tax_amount_after_discount_amount;
if (tax.category == 'Valuation') {
tax_amount = 0;
@ -420,13 +422,13 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
} else {
tax.total = flt(this.frm.doc["taxes"][row_idx-1].total + tax_amount, precision("total", tax));
}
},
}
_load_item_tax_rate: function(item_tax_rate) {
_load_item_tax_rate(item_tax_rate) {
return item_tax_rate ? JSON.parse(item_tax_rate) : {};
},
}
get_current_tax_amount: function(item, tax, item_tax_map) {
get_current_tax_amount(item, tax, item_tax_map) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
var current_tax_amount = 0.0;
@ -462,9 +464,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
return current_tax_amount;
},
}
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
set_item_wise_tax(item, tax, tax_rate, current_tax_amount) {
// store tax breakup for each item
let tax_detail = tax.item_wise_tax_detail;
let key = item.item_code || item.item_name;
@ -479,9 +481,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
item_wise_tax_amount += tax_detail[key][1];
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
},
}
round_off_totals: function(tax) {
round_off_totals(tax) {
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
tax.tax_amount= Math.round(tax.tax_amount);
tax.tax_amount_after_discount_amount = Math.round(tax.tax_amount_after_discount_amount);
@ -489,16 +491,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
},
}
round_off_base_values: function(tax) {
round_off_base_values(tax) {
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
tax.base_tax_amount= Math.round(tax.base_tax_amount);
tax.base_tax_amount_after_discount_amount = Math.round(tax.base_tax_amount_after_discount_amount);
}
},
}
manipulate_grand_total_for_inclusive_tax: function() {
manipulate_grand_total_for_inclusive_tax() {
var me = this;
// if fully inclusive taxes and diff
if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) {
@ -529,10 +531,10 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
}
}
},
}
calculate_totals: function() {
// Changing sequence can because of rounding adjustment issue and on-screen discrepancy
calculate_totals() {
// Changing sequence can cause rounding_adjustmentng issue and on-screen discrepency
var me = this;
var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0;
this.frm.doc.grand_total = flt(tax_count
@ -577,9 +579,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
// rounded totals
this.set_rounded_total();
},
}
set_rounded_total: function() {
set_rounded_total() {
var disable_rounded_total = 0;
if(frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total", this.frm.doc.name)) {
disable_rounded_total = this.frm.doc.disable_rounded_total;
@ -601,9 +603,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]);
}
},
}
_cleanup: function() {
_cleanup() {
this.frm.doc.base_in_words = this.frm.doc.in_words = "";
if(this.frm.doc["items"] && this.frm.doc["items"].length) {
@ -632,16 +634,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
this.frm.refresh_fields();
},
}
set_discount_amount: function() {
set_discount_amount() {
if(this.frm.doc.additional_discount_percentage) {
this.frm.doc.discount_amount = flt(flt(this.frm.doc[frappe.scrub(this.frm.doc.apply_discount_on)])
* this.frm.doc.additional_discount_percentage / 100, precision("discount_amount"));
}
},
}
apply_discount_amount: function() {
apply_discount_amount() {
var me = this;
var distributed_amount = 0.0;
this.frm.doc.base_discount_amount = 0.0;
@ -679,9 +681,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this._calculate_taxes_and_totals();
}
}
},
}
get_total_for_discount_amount: function() {
get_total_for_discount_amount() {
if(this.frm.doc.apply_discount_on == "Net Total") {
return this.frm.doc.net_total;
} else {
@ -705,27 +707,27 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
}
},
}
calculate_total_advance: function(update_paid_amount) {
calculate_total_advance(update_paid_amount) {
var total_allocated_amount = frappe.utils.sum($.map(this.frm.doc["advances"] || [], function(adv) {
return flt(adv.allocated_amount, precision("allocated_amount", adv));
}));
this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance"));
this.calculate_outstanding_amount(update_paid_amount);
},
}
is_internal_invoice: function() {
is_internal_invoice() {
if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) {
if (this.frm.doc.company === this.frm.doc.represents_company) {
return true;
}
}
return false;
},
}
calculate_outstanding_amount: function(update_paid_amount) {
calculate_outstanding_amount(update_paid_amount) {
// NOTE:
// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
// total_advance is only for non POS Invoice
@ -773,9 +775,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.frm.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
flt(this.frm.doc.change_amount * this.frm.doc.conversion_rate), precision("outstanding_amount"));
}
},
}
update_paid_amount_for_return: function() {
update_paid_amount_for_return() {
var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
@ -799,9 +801,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.frm.refresh_fields();
this.calculate_paid_amount();
},
}
set_default_payment: function(total_amount_to_pay, update_paid_amount) {
set_default_payment(total_amount_to_pay, update_paid_amount) {
var me = this;
var payment_status = true;
if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
@ -817,9 +819,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
});
}
},
}
calculate_paid_amount: function() {
calculate_paid_amount() {
var me = this;
var paid_amount = 0.0;
var base_paid_amount = 0.0;
@ -839,9 +841,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.frm.set_value('paid_amount', flt(paid_amount, precision("paid_amount")));
this.frm.set_value('base_paid_amount', flt(base_paid_amount, precision("base_paid_amount")));
},
}
calculate_change_amount: function(){
calculate_change_amount(){
this.frm.doc.change_amount = 0.0;
this.frm.doc.base_change_amount = 0.0;
if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)
@ -860,9 +862,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
precision("base_change_amount"));
}
}
},
}
calculate_write_off_amount: function(){
calculate_write_off_amount(){
if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount
+ this.frm.doc.change_amount, precision("write_off_amount"));
@ -874,4 +876,4 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
this.calculate_outstanding_amount(false);
}
});
};

View File

@ -3,9 +3,9 @@
frappe.provide('erpnext.accounts.dimensions');
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
setup: function() {
this._super();
erpnext.TransactionController = class TransactionController extends erpnext.taxes_and_totals {
setup() {
super.setup();
let me = this;
frappe.flags.hide_serial_batch_dialog = true;
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
@ -220,8 +220,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
},
onload: function() {
}
onload() {
var me = this;
if(this.frm.doc.__islocal) {
@ -247,15 +247,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
]);
}
},
}
is_return: function() {
is_return() {
if(!this.frm.doc.is_return && this.frm.doc.return_against) {
this.frm.set_value('return_against', '');
}
},
}
setup_quality_inspection: function() {
setup_quality_inspection() {
if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
return;
}
@ -296,9 +296,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
}
});
},
}
make_payment_request: function() {
make_payment_request() {
var me = this;
const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
? "Inward" : "Outward";
@ -320,9 +320,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
}
})
},
}
onload_post_render: function() {
onload_post_render() {
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
frappe.after_ajax(() => this.apply_default_taxes());
@ -334,9 +334,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.setup_item_selector();
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
}
},
}
refresh: function() {
refresh() {
erpnext.toggle_naming_series();
erpnext.hide_company();
this.set_dynamic_labels();
@ -366,9 +366,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
.appendTo($input_group);
}
}
},
}
scan_barcode: function() {
scan_barcode() {
let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
let show_description = function(idx, exist = null) {
@ -440,9 +440,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
return false;
},
}
apply_default_taxes: function() {
apply_default_taxes() {
var me = this;
var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges",
me.frm.doc.name);
@ -481,22 +481,22 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
});
}
},
}
setup_sms: function() {
setup_sms() {
var me = this;
let blacklist = ['Purchase Invoice', 'BOM'];
if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
&& !blacklist.includes(this.frm.doctype)) {
this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
}
},
}
send_sms: function() {
send_sms() {
var sms_man = new erpnext.SMSManager(this.frm.doc);
},
}
barcode: function(doc, cdt, cdn) {
barcode(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.barcode=="" || d.barcode==null) {
// barcode cleared, remove item
@ -505,9 +505,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
this.item_code(doc, cdt, cdn);
},
}
item_code: function(doc, cdt, cdn) {
item_code(doc, cdt, cdn) {
var me = this;
var item = frappe.get_doc(cdt, cdn);
var update_stock = 0, show_batch_dialog = 0;
@ -658,9 +658,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
}
},
}
price_list_rate: function(doc, cdt, cdn) {
price_list_rate(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
@ -672,17 +672,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
precision("rate", item));
this.calculate_taxes_and_totals();
},
}
margin_rate_or_amount: function(doc, cdt, cdn) {
margin_rate_or_amount(doc, cdt, cdn) {
// calculated the revised total margin and rate on margin rate changes
let item = frappe.get_doc(cdt, cdn);
this.apply_pricing_rule_on_item(item);
this.calculate_taxes_and_totals();
cur_frm.refresh_fields();
},
}
margin_type: function(doc, cdt, cdn) {
margin_type(doc, cdt, cdn) {
// calculate the revised total margin and rate on margin type changes
let item = frappe.get_doc(cdt, cdn);
if (!item.margin_type) {
@ -692,9 +692,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.calculate_taxes_and_totals();
cur_frm.refresh_fields();
}
},
}
get_incoming_rate: function(item, posting_date, posting_time, voucher_type, company) {
get_incoming_rate(item, posting_date, posting_time, voucher_type, company) {
let item_args = {
'item_code': item.item_code,
@ -717,9 +717,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
}
});
},
}
serial_no: function(doc, cdt, cdn) {
add_taxes_from_item_tax_template(item_tax_map) {
let me = this;
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
if(typeof (item_tax_map) == "string") {
item_tax_map = JSON.parse(item_tax_map);
}
$.each(item_tax_map, function(tax, rate) {
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
if(!found) {
let child = frappe.model.add_child(me.frm.doc, "taxes");
child.charge_type = "On Net Total";
child.account_head = tax;
child.rate = 0;
}
});
}
}
serial_no(doc, cdt, cdn) {
var me = this;
var item = frappe.get_doc(cdt, cdn);
@ -743,9 +763,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
}
}
},
}
update_qty: function(cdt, cdn) {
update_qty(cdt, cdn) {
var valid_serial_nos = [];
var serialnos = [];
var item = frappe.get_doc(cdt, cdn);
@ -758,17 +778,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.model.set_value(item.doctype, item.name,
"qty", valid_serial_nos.length / item.conversion_factor);
frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
},
}
validate: function() {
validate() {
this.calculate_taxes_and_totals(false);
},
}
update_stock: function() {
update_stock() {
this.frm.trigger('set_default_internal_warehouse');
},
}
set_default_internal_warehouse: function() {
set_default_internal_warehouse() {
let me = this;
if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
|| this.frm.doc.doctype == 'Delivery Note') {
@ -787,9 +807,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
}
},
}
company: function() {
company() {
var me = this;
var set_pricing = function() {
if(me.frm.doc.company && me.frm.fields_dict.currency) {
@ -893,16 +913,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(this.frm.doc.company) {
erpnext.last_selected_company = this.frm.doc.company;
}
},
}
transaction_date: function() {
transaction_date() {
if (this.frm.doc.transaction_date) {
this.frm.transaction_date = this.frm.doc.transaction_date;
frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
}
},
}
posting_date: function() {
posting_date() {
var me = this;
if (this.frm.doc.posting_date) {
this.frm.posting_date = this.frm.doc.posting_date;
@ -931,9 +951,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.ui.form.trigger(me.frm.doc.doctype, "currency");
}
}
},
}
due_date: function() {
due_date() {
// due_date is to be changed, payment terms template and/or payment schedule must
// be removed as due_date is automatically changed based on payment terms
if (this.frm.doc.due_date && !this.frm.updating_party_details && !this.frm.doc.is_pos) {
@ -956,13 +976,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.msgprint(final_message);
}
}
},
}
bill_date: function() {
bill_date() {
this.posting_date();
},
}
recalculate_terms: function() {
recalculate_terms() {
const doc = this.frm.doc;
if (doc.payment_terms_template) {
this.payment_terms_template();
@ -981,17 +1001,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
);
}
},
}
get_company_currency: function() {
get_company_currency() {
return erpnext.get_currency(this.frm.doc.company);
},
}
contact_person: function() {
contact_person() {
erpnext.utils.get_contact_details(this.frm);
},
}
currency: function() {
currency() {
/* manqala 19/09/2016: let the translation date be whichever of the transaction_date or posting_date is available */
var transaction_date = this.frm.doc.transaction_date || this.frm.doc.posting_date;
/* end manqala */
@ -1013,9 +1033,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} else {
this.conversion_rate();
}
},
}
conversion_rate: function() {
conversion_rate() {
const me = this.frm;
if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0);
@ -1035,9 +1055,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
// Make read only if Accounts Settings doesn't allow stale rates
this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
},
}
shipping_rule: function() {
shipping_rule() {
var me = this;
if(this.frm.doc.shipping_rule) {
return this.frm.call({
@ -1053,9 +1073,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
else {
me.calculate_taxes_and_totals();
}
},
}
set_margin_amount_based_on_currency: function(exchange_rate) {
set_margin_amount_based_on_currency(exchange_rate) {
if (in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Purchase Invoice", "Purchase Order", "Purchase Receipt"]), this.frm.doc.doctype) {
var me = this;
$.each(this.frm.doc.items || [], function(i, d) {
@ -1065,9 +1085,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
});
}
},
}
set_actual_charges_based_on_currency: function(exchange_rate) {
set_actual_charges_based_on_currency(exchange_rate) {
var me = this;
$.each(this.frm.doc.taxes || [], function(i, d) {
if(d.charge_type == "Actual") {
@ -1075,9 +1095,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
flt(d.tax_amount) / flt(exchange_rate));
}
});
},
}
get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) {
get_exchange_rate(transaction_date, from_currency, to_currency, callback) {
var args;
if (["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"].includes(this.frm.doctype)) {
args = "for_selling";
@ -1101,9 +1121,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
callback(flt(r.message));
}
});
},
}
price_list_currency: function() {
price_list_currency() {
var me=this;
this.set_dynamic_labels();
@ -1117,9 +1137,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} else {
this.plc_conversion_rate();
}
},
}
plc_conversion_rate: function() {
plc_conversion_rate() {
if(this.frm.doc.price_list_currency === this.get_company_currency()) {
this.frm.set_value("plc_conversion_rate", 1.0);
} else if(this.frm.doc.price_list_currency === this.frm.doc.currency
@ -1131,9 +1151,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(!this.in_apply_price_list) {
this.apply_price_list(null, true);
}
},
}
uom: function(doc, cdt, cdn) {
uom(doc, cdt, cdn) {
var me = this;
var item = frappe.get_doc(cdt, cdn);
if(item.item_code && item.uom) {
@ -1151,9 +1171,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
me.calculate_stock_uom_rate(doc, cdt, cdn);
},
}
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
@ -1178,35 +1198,35 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
this.calculate_stock_uom_rate(doc, cdt, cdn);
}
},
}
batch_no: function(doc, cdt, cdn) {
batch_no(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
this.apply_price_list(item, true);
},
}
toggle_conversion_factor: function(item) {
toggle_conversion_factor(item) {
// toggle read only property for conversion factor field if the uom and stock uom are same
if(this.frm.get_field('items').grid.fields_map.conversion_factor) {
this.frm.fields_dict.items.grid.toggle_enable("conversion_factor",
((item.uom != item.stock_uom) && !frappe.meta.get_docfield(cur_frm.fields_dict.items.grid.doctype, "conversion_factor").read_only)? true: false);
}
},
}
qty: function(doc, cdt, cdn) {
qty(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
this.conversion_factor(doc, cdt, cdn, true);
this.calculate_stock_uom_rate(doc, cdt, cdn);
this.apply_pricing_rule(item, true);
},
}
calculate_stock_uom_rate: function(doc, cdt, cdn) {
calculate_stock_uom_rate(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);
refresh_field("stock_uom_rate", item.name, item.parentfield);
},
service_stop_date: function(frm, cdt, cdn) {
}
service_stop_date(frm, cdt, cdn) {
var child = locals[cdt][cdn];
if(child.service_stop_date) {
@ -1222,9 +1242,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.throw(__("Service Stop Date cannot be after Service End Date"));
}
}
},
}
service_start_date: function(frm, cdt, cdn) {
service_start_date(frm, cdt, cdn) {
var child = locals[cdt][cdn];
if(child.service_start_date) {
@ -1236,9 +1256,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
})
}
},
}
calculate_net_weight: function(){
calculate_net_weight(){
/* Calculate Total Net Weight then further applied shipping rule to calculate shipping charges.*/
var me = this;
this.frm.doc.total_net_weight= 0.0;
@ -1248,9 +1268,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
refresh_field("total_net_weight");
this.shipping_rule();
},
}
set_dynamic_labels: function() {
set_dynamic_labels() {
// What TODO? should we make price list system non-mandatory?
this.frm.toggle_reqd("plc_conversion_rate",
!!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency));
@ -1259,9 +1279,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.change_form_labels(company_currency);
this.change_grid_labels(company_currency);
this.frm.refresh_fields();
},
}
change_form_labels: function(company_currency) {
change_form_labels(company_currency) {
var me = this;
this.frm.set_currency_labels(["base_total", "base_net_total", "base_total_taxes_and_charges",
@ -1308,9 +1328,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(frappe.meta.get_docfield(cur_frm.doctype, "base_net_total"))
cur_frm.toggle_display("base_net_total", (show && (me.frm.doc.currency != company_currency)));
},
}
change_grid_labels: function(company_currency) {
change_grid_labels(company_currency) {
var me = this;
this.update_item_grid_labels(company_currency);
@ -1351,9 +1371,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
this.update_payment_schedule_grid_labels(company_currency);
},
}
update_item_grid_labels: function(company_currency) {
update_item_grid_labels(company_currency) {
this.frm.set_currency_labels([
"base_rate", "base_net_rate", "base_price_list_rate",
"base_amount", "base_net_amount", "base_rate_with_margin"
@ -1363,9 +1383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"rate", "net_rate", "price_list_rate", "amount",
"net_amount", "stock_uom_rate", "rate_with_margin"
], this.frm.doc.currency, "items");
},
}
update_payment_schedule_grid_labels: function(company_currency) {
update_payment_schedule_grid_labels(company_currency) {
const me = this;
if (this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length > 0) {
this.frm.set_currency_labels(["base_payment_amount", "base_outstanding", "base_paid_amount"],
@ -1379,9 +1399,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
schedule_grid.set_column_disp(fname, me.frm.doc.currency != company_currency);
});
}
},
}
toggle_item_grid_columns: function(company_currency) {
toggle_item_grid_columns(company_currency) {
const me = this;
// toggle columns
var item_grid = this.frm.fields_dict["items"].grid;
@ -1402,21 +1422,21 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(frappe.meta.get_docfield(item_grid.doctype, fname))
item_grid.set_column_disp(fname, (show && (me.frm.doc.currency != company_currency)));
});
},
}
recalculate: function() {
recalculate() {
this.calculate_taxes_and_totals();
},
}
recalculate_values: function() {
recalculate_values() {
this.calculate_taxes_and_totals();
},
}
calculate_charges: function() {
calculate_charges() {
this.calculate_taxes_and_totals();
},
}
ignore_pricing_rule: function() {
ignore_pricing_rule() {
if(this.frm.doc.ignore_pricing_rule) {
var me = this;
var item_list = [];
@ -1450,9 +1470,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} else {
this.apply_pricing_rule();
}
},
}
apply_pricing_rule: function(item, calculate_taxes_and_totals) {
apply_pricing_rule(item, calculate_taxes_and_totals) {
var me = this;
var args = this._get_args(item);
if (!(args.items && args.items.length)) {
@ -1471,9 +1491,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
}
});
},
}
_get_args: function(item) {
_get_args(item) {
var me = this;
return {
"items": this._get_item_list(item),
@ -1501,9 +1521,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
"coupon_code": me.frm.doc.coupon_code
};
},
}
_get_item_list: function(item) {
_get_item_list(item) {
var item_list = [];
var append_item = function(d) {
if (d.item_code) {
@ -1544,9 +1564,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
return item_list;
},
}
_set_values_for_item_list: function(children) {
_set_values_for_item_list(children) {
var me = this;
var price_list_rate_changed = false;
var items_rule_dict = {};
@ -1586,9 +1606,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
me.apply_rule_on_other_items(items_rule_dict);
if(!price_list_rate_changed) me.calculate_taxes_and_totals();
},
}
apply_rule_on_other_items: function(args) {
apply_rule_on_other_items(args) {
const me = this;
const fields = ["discount_percentage", "pricing_rules", "discount_amount", "rate"];
@ -1607,9 +1627,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
}
}
},
}
apply_product_discount: function(args) {
apply_product_discount(args) {
const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];
const exist_items = items.map(row => (row.item_code, row.pricing_rules));
@ -1634,9 +1654,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// free_item_data is a temporary variable
args.free_item_data = '';
refresh_field('items');
},
}
apply_price_list: function(item, reset_plc_conversion) {
apply_price_list(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
if (!reset_plc_conversion) {
@ -1675,9 +1695,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}).always(() => {
me.in_apply_price_list = false;
});
},
}
remove_pricing_rule: function(item) {
remove_pricing_rule(item) {
let me = this;
const fields = ["discount_percentage",
"discount_amount", "margin_rate_or_amount", "rate_with_margin"];
@ -1711,18 +1731,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
me.trigger_price_list_rate();
}
},
}
trigger_price_list_rate: function() {
trigger_price_list_rate() {
var me = this;
this.frm.doc.items.forEach(child_row => {
me.frm.script_manager.trigger("price_list_rate",
child_row.doctype, child_row.name);
})
},
}
validate_company_and_party: function() {
validate_company_and_party() {
var me = this;
var valid = true;
@ -1737,9 +1757,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
});
return valid;
},
}
get_terms: function() {
get_terms() {
var me = this;
erpnext.utils.get_terms(this.frm.doc.tc_name, this.frm.doc, function(r) {
@ -1747,9 +1767,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
me.frm.set_value("terms", r.message);
}
});
},
}
taxes_and_charges: function() {
taxes_and_charges() {
var me = this;
if(this.frm.doc.taxes_and_charges) {
return this.frm.call({
@ -1775,9 +1795,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
});
}
},
}
tax_category: function() {
tax_category() {
var me = this;
if(me.frm.updating_party_details) return;
@ -1785,9 +1805,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
() => this.update_item_tax_map(),
() => erpnext.utils.set_taxes(this.frm, "tax_category"),
]);
},
}
item_tax_template: function(doc, cdt, cdn) {
item_tax_template(doc, cdt, cdn) {
var me = this;
if(me.frm.updating_party_details) return;
@ -1812,11 +1832,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
item.item_tax_rate = "{}";
me.calculate_taxes_and_totals();
}
},
}
is_recurring: function() {
is_recurring() {
// set default values for recurring documents
if(this.frm.doc.is_recurring && this.frm.doc.__islocal) {
frappe.msgprint(__("Please set recurring after saving"));
@ -1839,9 +1858,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
},
}
from_date: function() {
from_date() {
// set to_date
if(this.frm.doc.from_date) {
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
@ -1855,25 +1874,25 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
refresh_field('to_date');
}
}
},
}
set_gross_profit: function(item) {
set_gross_profit(item) {
if (["Sales Order", "Quotation"].includes(this.frm.doc.doctype) && item.valuation_rate) {
var rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1);
item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item));
}
},
}
setup_item_selector: function() {
setup_item_selector() {
// TODO: remove item selector
return;
// if(!this.item_selector) {
// this.item_selector = new erpnext.ItemSelector({frm: this.frm});
// }
},
}
get_advances: function() {
get_advances() {
if(!this.frm.is_return) {
return this.frm.call({
method: "set_advances",
@ -1883,9 +1902,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
})
}
},
}
make_payment_entry: function() {
make_payment_entry() {
return frappe.call({
method: cur_frm.cscript.get_method_for_payment(),
args: {
@ -1898,9 +1917,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// cur_frm.refresh_fields()
}
});
},
}
make_quality_inspection: function () {
make_quality_inspection() {
let data = [];
const fields = [
{
@ -2022,9 +2041,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} else {
dialog.show();
}
},
}
get_method_for_payment: function(){
get_method_for_payment() {
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){
if(in_list(['Sales Invoice', 'Purchase Invoice'], cur_frm.doc.doctype)){
@ -2035,9 +2054,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
return method
},
}
set_query_for_batch: function(doc, cdt, cdn) {
set_query_for_batch(doc, cdt, cdn) {
// Show item's batches in the dropdown of batch no
var me = this;
@ -2067,9 +2086,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
filters: filters
}
}
},
}
set_query_for_item_tax_template: function(doc, cdt, cdn) {
set_query_for_item_tax_template(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(!item.item_code) {
return doc.company ? {filters: {company: doc.company}} : {};
@ -2089,9 +2108,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
filters: filters
}
}
},
}
payment_terms_template: function() {
payment_terms_template() {
var me = this;
const doc = this.frm.doc;
if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') {
@ -2114,9 +2133,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
})
}
},
}
payment_term: function(doc, cdt, cdn) {
payment_term(doc, cdt, cdn) {
const me = this;
var row = locals[cdt][cdn];
if(row.payment_term) {
@ -2140,17 +2159,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
})
}
},
}
against_blanket_order: function(doc, cdt, cdn) {
against_blanket_order(doc, cdt, cdn) {
var item = locals[cdt][cdn];
if(!item.against_blanket_order) {
frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order", null);
frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order_rate", 0.00);
}
},
}
blanket_order: function(doc, cdt, cdn) {
blanket_order(doc, cdt, cdn) {
var me = this;
var item = locals[cdt][cdn];
if (item.blanket_order && (item.parenttype=="Sales Order" || item.parenttype=="Purchase Order")) {
@ -2178,34 +2197,34 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
})
}
},
}
set_reserve_warehouse: function() {
set_reserve_warehouse() {
this.autofill_warehouse(this.frm.doc.supplied_items, "reserve_warehouse", this.frm.doc.set_reserve_warehouse);
},
}
set_warehouse: function() {
set_warehouse() {
this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
},
}
set_target_warehouse: function() {
set_target_warehouse() {
this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
},
}
set_from_warehouse: function() {
set_from_warehouse() {
this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
},
}
autofill_warehouse : function (child_table, warehouse_field, warehouse) {
autofill_warehouse(child_table, warehouse_field, warehouse) {
if (warehouse && child_table && child_table.length) {
let doctype = child_table[0].doctype;
$.each(child_table || [], function(i, item) {
frappe.model.set_value(doctype, item.name, warehouse_field, warehouse);
});
}
},
}
coupon_code: function() {
coupon_code() {
var me = this;
frappe.run_serially([
() => this.frm.doc.ignore_pricing_rule=1,
@ -2214,7 +2233,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
() => me.apply_pricing_rule()
]);
}
});
};
erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_dialog) {
let warehouse, receiving_stock, existing_stock;

View File

@ -0,0 +1,2 @@
import "./website_utils";
import "./shopping_cart";

View File

@ -0,0 +1,27 @@
import "./conf";
import "./utils";
import "./queries";
import "./sms_manager";
import "./utils/party";
import "./controllers/stock_controller";
import "./payment/payments";
import "./controllers/taxes_and_totals";
import "./controllers/transaction";
import "./templates/item_selector.html";
import "./templates/employees_to_mark_attendance.html";
import "./utils/item_selector";
import "./help_links";
import "./agriculture/ternary_plot";
import "./templates/item_quick_entry.html";
import "./utils/item_quick_entry";
import "./utils/customer_quick_entry";
import "./education/student_button.html";
import "./education/assessment_result_tool.html";
import "./hub/hub_factory";
import "./call_popup/call_popup";
import "./utils/dimension_tree_filter";
import "./telephony";
import "./templates/call_link.html";
// import { sum } from 'frappe/public/utils/util.js'

View File

@ -19,11 +19,7 @@ frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.
}
make(page_name) {
const assets = [
'/assets/js/marketplace.min.js'
];
frappe.require(assets, () => {
frappe.require('marketplace.bundle.js', () => {
erpnext.hub.marketplace = new erpnext.hub.Marketplace({
parent: this.make_page(true, page_name)
});

View File

@ -0,0 +1,5 @@
import "../../stock/dashboard/item_dashboard.html";
import "../../stock/dashboard/item_dashboard_list.html";
import "../../stock/dashboard/item_dashboard.js";
import "../../stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html";
import "../../stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html";

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.payments = erpnext.stock.StockController.extend({
make_payment: function() {
erpnext.payments = class payments extends erpnext.stock.StockController {
make_payment() {
var me = this;
this.dialog = new frappe.ui.Dialog({
@ -14,15 +14,15 @@ erpnext.payments = erpnext.stock.StockController.extend({
this.set_payment_primary_action();
this.make_keyboard();
this.select_text();
},
}
select_text() {
$(this.$body).find('.form-control').click(function() {
$(this).select();
});
},
}
set_payment_primary_action: function() {
set_payment_primary_action() {
var me = this;
this.dialog.set_primary_action(__("Submit"), function() {
@ -35,18 +35,18 @@ erpnext.payments = erpnext.stock.StockController.extend({
}
});
})
},
}
make_keyboard: function() {
make_keyboard(){
var me = this;
$(this.$body).empty();
$(this.$body).html(frappe.render_template('pos_payment', this.frm.doc))
this.show_payment_details();
this.bind_keyboard_event();
this.clear_amount();
},
this.bind_keyboard_event()
this.clear_amount()
}
make_multimode_payment: function() {
make_multimode_payment(){
var me = this;
if (this.frm.doc.change_amount > 0) {
@ -56,9 +56,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments");
this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value();
this.payments.amount = flt(this.payment_val);
},
}
show_payment_details: function() {
show_payment_details(){
var me = this;
var multimode_payments = $(this.$body).find('.multimode-payments').empty();
if (this.frm.doc.payments.length) {
@ -81,9 +81,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
}else{
$("<p>No payment mode selected in pos profile</p>").appendTo(multimode_payments)
}
},
}
set_outstanding_amount: function() {
set_outstanding_amount(){
this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx}));
this.highlight_selected_row();
this.payment_val = 0.0;
@ -98,15 +98,16 @@ erpnext.payments = erpnext.stock.StockController.extend({
}
this.selected_mode.select()
this.bind_amount_change_event();
},
}
bind_keyboard_event() {
bind_keyboard_event(){
var me = this;
this.payment_val = '';
this.bind_form_control_event();
this.bind_numeric_keys_event();
},
}
bind_form_control_event: function() {
bind_form_control_event() {
var me = this;
$(this.$body).find('.pos-payment-row').click(function() {
me.idx = $(this).attr("idx");
@ -126,7 +127,7 @@ erpnext.payments = erpnext.stock.StockController.extend({
$(this.$body).find('.change_amount').change(function() {
me.change_amount(flt($(this).val()), precision("change_amount"));
});
},
}
highlight_selected_row() {
var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']", {'idx': this.idx}));
@ -134,9 +135,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
selected_row.addClass('selected-payment-mode');
$(this.$body).find('.amount').attr('disabled', true);
this.selected_mode.attr('disabled', false);
},
}
bind_numeric_keys_event: function() {
bind_numeric_keys_event() {
var me = this;
$(this.$body).find('.pos-keyboard-key').click(function(){
me.payment_val += $(this).text();
@ -152,7 +153,7 @@ erpnext.payments = erpnext.stock.StockController.extend({
me.update_paid_amount();
})
},
}
bind_amount_change_event() {
var me = this;
@ -162,9 +163,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
me.idx = me.selected_mode.attr("idx");
me.update_payment_amount();
});
},
}
clear_amount: function() {
clear_amount() {
var me = this;
$(this.$body).find('.clr').click(function(e) {
e.stopPropagation();
@ -175,7 +176,7 @@ erpnext.payments = erpnext.stock.StockController.extend({
me.highlight_selected_row();
me.update_payment_amount();
});
},
}
write_off_amount(write_off_amount) {
this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount"));
@ -183,17 +184,17 @@ erpnext.payments = erpnext.stock.StockController.extend({
precision("base_write_off_amount"));
this.calculate_outstanding_amount(false);
this.show_amounts();
},
}
change_amount: function(change_amount) {
change_amount(change_amount) {
var me = this;
this.frm.doc.change_amount = flt(change_amount, precision("change_amount"));
this.calculate_write_off_amount();
this.show_amounts();
},
}
update_paid_amount: function(update_write_off) {
update_paid_amount(update_write_off) {
var me = this;
if (in_list(['change_amount', 'write_off_amount'], this.idx)) {
var value = me.selected_mode.val();
@ -208,9 +209,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
} else {
this.update_payment_amount();
}
},
}
update_payment_amount: function() {
update_payment_amount(){
var me = this;
$.each(this.frm.doc.payments, function(index, data) {
@ -221,9 +222,9 @@ erpnext.payments = erpnext.stock.StockController.extend({
this.calculate_outstanding_amount(false);
this.show_amounts();
},
}
show_amounts: function() {
show_amounts(){
var me = this;
$(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency));
$(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency));
@ -231,4 +232,4 @@ erpnext.payments = erpnext.stock.StockController.extend({
$(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency));
this.update_invoice();
}
})
}

View File

@ -0,0 +1,8 @@
import "../../selling/page/point_of_sale/pos_item_selector.js";
import "../../selling/page/point_of_sale/pos_item_cart.js";
import "../../selling/page/point_of_sale/pos_item_details.js";
import "../../selling/page/point_of_sale/pos_number_pad.js";
import "../../selling/page/point_of_sale/pos_payment.js";
import "../../selling/page/point_of_sale/pos_past_order_list.js";
import "../../selling/page/point_of_sale/pos_past_order_summary.js";
import "../../selling/page/point_of_sale/pos_controller.js";

View File

@ -2,8 +2,8 @@
// License: GNU General Public License v3. See license.txt
erpnext.StockAnalytics = erpnext.StockGridReport.extend({
init: function(wrapper, opts) {
erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport {
constructor(wrapper, opts) {
var args = {
title: __("Stock Analytics"),
parent: $(wrapper).find('.layout-main'),
@ -30,9 +30,33 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
if(opts) $.extend(args, opts);
this._super(args);
},
setup_columns: function() {
super(args);
this.filters = [
{fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}},
{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
default_value: __("Select Brand..."), filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val || item._show;
}, link_formatter: {filter_input: "brand"}},
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
default_value: __("Select Warehouse...")},
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
{fieldtype:"Select", label: __("Range"), fieldname: "range",
options:[
{label:__("Daily"), value:"Daily"},
{label:__("Weekly"), value:"Weekly"},
{label:__("Monthly"), value:"Monthly"},
{label:__("Quarterly"), value:"Quarterly"},
{label:__("Yearly"), value:"Yearly"},
]}
];
}
setup_columns() {
var std_columns = [
{id: "name", name: __("Item"), field: "name", width: 300},
{id: "brand", name: __("Brand"), field: "brand", width: 100},
@ -43,43 +67,21 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
this.make_date_range_columns();
this.columns = std_columns.concat(this.columns);
},
filters: [
{fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}},
{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
default_value: __("Select Brand..."), filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val || item._show;
}, link_formatter: {filter_input: "brand"}},
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
default_value: __("Select Warehouse...")},
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
{fieldtype:"Select", label: __("Range"), fieldname: "range",
options:[
{label:__("Daily"), value:"Daily"},
{label:__("Weekly"), value:"Weekly"},
{label:__("Monthly"), value:"Monthly"},
{label:__("Quarterly"), value:"Quarterly"},
{label:__("Yearly"), value:"Yearly"},
]}
],
setup_filters: function() {
}
setup_filters() {
var me = this;
this._super();
super.setup_filters();
this.trigger_refresh_on_change(["value_or_qty", "brand", "warehouse", "range"]);
this.show_zero_check();
},
init_filter_values: function() {
this._super();
}
init_filter_values() {
super.init_filter_values();
this.filter_inputs.range && this.filter_inputs.range.val('Monthly');
},
prepare_data: function() {
}
prepare_data() {
var me = this;
if(!this.data) {
@ -112,8 +114,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
this.prepare_balances();
this.update_groups();
},
prepare_balances: function() {
}
prepare_balances() {
var me = this;
var from_date = frappe.datetime.str_to_obj(this.from_date);
var to_date = frappe.datetime.str_to_obj(this.to_date);
@ -164,8 +166,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
item.closing_qty_value += diff;
}
}
},
update_groups: function() {
}
update_groups() {
var me = this;
$.each(this.data, function(i, item) {
// update groups
@ -192,8 +194,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
}
}
});
},
show_stock_ledger: function(item_code) {
}
show_stock_ledger(item_code) {
frappe.route_options = {
item_code: item_code,
from_date: this.from_date,
@ -201,5 +203,5 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
};
frappe.set_route("query-report", "Stock Ledger");
}
});
};

View File

@ -1,16 +1,16 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
get_item_warehouse: function(warehouse, item) {
erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridReport {
get_item_warehouse(warehouse, item) {
if(!this.item_warehouse[item]) this.item_warehouse[item] = {};
if(!this.item_warehouse[item][warehouse]) this.item_warehouse[item][warehouse] = {
balance_qty: 0.0, balance_value: 0.0, fifo_stack: []
};
return this.item_warehouse[item][warehouse];
},
}
get_value_diff: function(wh, sl, is_fifo) {
get_value_diff(wh, sl, is_fifo) {
// value
if(sl.qty > 0) {
// incoming - rate is given
@ -59,8 +59,8 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
wh.balance_qty += sl.qty;
wh.balance_value += value_diff;
return value_diff;
},
get_fifo_value_diff: function(wh, sl) {
}
get_fifo_value_diff(wh, sl) {
// get exact rate from fifo stack
var fifo_stack = (wh.fifo_stack || []).reverse();
var fifo_value_diff = 0.0;
@ -89,9 +89,9 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
// reset the updated stack
wh.fifo_stack = fifo_stack.reverse();
return -fifo_value_diff;
},
}
get_serialized_value_diff: function(sl) {
get_serialized_value_diff(sl) {
var me = this;
var value_diff = 0.0;
@ -103,9 +103,9 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
});
return value_diff;
},
}
get_serialized_buying_rates: function() {
get_serialized_buying_rates() {
var serialized_buying_rates = {};
if (frappe.report_dump.data["Serial No"]) {
@ -115,5 +115,5 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
}
return serialized_buying_rates;
},
});
}
};

Some files were not shown because too many files have changed in this diff Show More