mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-17 20:02:38 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa0adafa82 | ||
|
|
99f4b43641 | ||
|
|
fdeab29e94 | ||
|
|
31755b485f | ||
|
|
3f3696d1eb | ||
|
|
e1a478779c | ||
|
|
6c6f3789d0 | ||
|
|
044c43a5cb | ||
|
|
9ce9c052e4 | ||
|
|
83e68bb837 | ||
|
|
415df04834 | ||
|
|
6d2d6862d6 | ||
|
|
13a65d52dd | ||
|
|
6485d4a749 | ||
|
|
2e63c80523 | ||
|
|
23bd21778e | ||
|
|
a3f490890d | ||
|
|
8e3ea32d6d | ||
|
|
c4a1a943ef | ||
|
|
abc0b64b68 | ||
|
|
00818bfa90 | ||
|
|
b9bfe6117e | ||
|
|
7a9f46d9d1 | ||
|
|
a10b52c6e6 | ||
|
|
5033e7b431 | ||
|
|
e6791ee78e | ||
|
|
b84ba868e6 | ||
|
|
1c501b6aac | ||
|
|
9a2a6d8fcb | ||
|
|
0b59d1c78b | ||
|
|
0fbf10797c | ||
|
|
58344cbb81 | ||
|
|
c6e2c8f79e | ||
|
|
b64b461d53 | ||
|
|
0b93bdcf40 | ||
|
|
e3910d02a5 | ||
|
|
0af146cea6 | ||
|
|
683f756d0f | ||
|
|
d905204e49 | ||
|
|
d8bc40d7f0 | ||
|
|
54059b77a0 | ||
|
|
f9f0e2591f | ||
|
|
5810bf70c7 | ||
|
|
97d8db775e | ||
|
|
fbe08ec7d0 | ||
|
|
0045c305ac | ||
|
|
5dd0fb6e2a | ||
|
|
e99fff8d08 | ||
|
|
b61fed9106 | ||
|
|
398c83afa5 | ||
|
|
c346484ca4 | ||
|
|
70b7f7f036 | ||
|
|
31c51ef914 | ||
|
|
be464696cc | ||
|
|
191b2970e9 | ||
|
|
c50f033722 | ||
|
|
e1e1414894 | ||
|
|
d1441245fb | ||
|
|
87da662703 | ||
|
|
99ba924303 | ||
|
|
066ff9599a | ||
|
|
33ebaf479d | ||
|
|
3a573d1a6d | ||
|
|
7f66983309 | ||
|
|
2c867fdd73 | ||
|
|
95dfc2730b | ||
|
|
558646c6b8 | ||
|
|
ea0d98891f | ||
|
|
1b4c5ad1e1 | ||
|
|
2678135f5e | ||
|
|
25ef4ff373 |
@@ -1,2 +1,2 @@
|
||||
from __future__ import unicode_literals
|
||||
__version__ = '6.5.2'
|
||||
__version__ = '6.6.5'
|
||||
|
||||
@@ -147,6 +147,8 @@ class Account(Document):
|
||||
self.validate_warehouse(old_warehouse)
|
||||
if self.warehouse:
|
||||
self.validate_warehouse(self.warehouse)
|
||||
elif self.warehouse:
|
||||
self.warehouse = None
|
||||
|
||||
def validate_warehouse(self, warehouse):
|
||||
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}):
|
||||
|
||||
@@ -6,7 +6,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.party import validate_party_gle_currency, get_party_account_currency
|
||||
from erpnext.accounts.party import validate_party_gle_currency
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.setup.doctype.company.company import get_company_currency
|
||||
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
|
||||
@@ -114,13 +114,7 @@ class GLEntry(Document):
|
||||
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
|
||||
|
||||
if self.party_type and self.party:
|
||||
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
|
||||
|
||||
if party_account_currency != self.account_currency:
|
||||
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
|
||||
|
||||
validate_party_gle_currency(self.party_type, self.party, self.company)
|
||||
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
|
||||
|
||||
def validate_balance_type(account, adv_adj=False):
|
||||
if not adv_adj and account:
|
||||
|
||||
@@ -8,10 +8,10 @@ frappe.require("assets/erpnext/js/utils.js");
|
||||
frappe.ui.form.on("Journal Entry", {
|
||||
refresh: function(frm) {
|
||||
erpnext.toggle_naming_series();
|
||||
cur_frm.cscript.voucher_type(frm.doc);
|
||||
frm.cscript.voucher_type(frm.doc);
|
||||
|
||||
if(frm.doc.docstatus==1) {
|
||||
cur_frm.add_custom_button(__('View Ledger'), function() {
|
||||
frm.add_custom_button(__('View Ledger'), function() {
|
||||
frappe.route_options = {
|
||||
"voucher_no": frm.doc.name,
|
||||
"from_date": frm.doc.posting_date,
|
||||
@@ -22,34 +22,22 @@ frappe.ui.form.on("Journal Entry", {
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}, "icon-table");
|
||||
}
|
||||
|
||||
|
||||
if (frm.doc.__islocal) {
|
||||
frm.add_custom_button(__('Quick Entry'), function() {
|
||||
return erpnext.journal_entry.quick_entry(frm);
|
||||
});
|
||||
}
|
||||
|
||||
// hide /unhide fields based on currency
|
||||
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
|
||||
},
|
||||
|
||||
|
||||
multi_currency: function(frm) {
|
||||
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
|
||||
}
|
||||
})
|
||||
|
||||
erpnext.journal_entry.toggle_fields_based_on_currency = function(frm) {
|
||||
var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"];
|
||||
|
||||
var grid = frm.get_field("accounts").grid;
|
||||
if(grid) grid.set_column_disp(fields, frm.doc.multi_currency);
|
||||
|
||||
// dynamic label
|
||||
var field_label_map = {
|
||||
"debit_in_account_currency": "Debit",
|
||||
"credit_in_account_currency": "Credit"
|
||||
};
|
||||
|
||||
$.each(field_label_map, function (fieldname, label) {
|
||||
var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
|
||||
df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
|
||||
})
|
||||
}
|
||||
|
||||
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
this.load_defaults();
|
||||
@@ -69,28 +57,19 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
);
|
||||
|
||||
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = get_today();
|
||||
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || get_today();
|
||||
}
|
||||
},
|
||||
|
||||
setup_queries: function() {
|
||||
var me = this;
|
||||
|
||||
|
||||
me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
|
||||
var filters = {
|
||||
company: me.frm.doc.company,
|
||||
is_group: 0
|
||||
};
|
||||
if(!doc.multi_currency) {
|
||||
$.extend(filters, {
|
||||
account_currency: frappe.get_doc(":Company", me.frm.doc.company).default_currency
|
||||
});
|
||||
}
|
||||
return { filters: filters };
|
||||
return erpnext.journal_entry.account_query(me.frm);
|
||||
});
|
||||
|
||||
|
||||
me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
|
||||
return {
|
||||
return {
|
||||
filters: {
|
||||
company: me.frm.doc.company,
|
||||
is_group: 0
|
||||
@@ -132,22 +111,22 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
|
||||
if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) {
|
||||
out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]);
|
||||
|
||||
|
||||
// account filter
|
||||
frappe.model.validate_missing(jvd, "account");
|
||||
|
||||
|
||||
party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
|
||||
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
|
||||
} else {
|
||||
// party_type and party mandatory
|
||||
frappe.model.validate_missing(jvd, "party_type");
|
||||
frappe.model.validate_missing(jvd, "party");
|
||||
|
||||
|
||||
out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
|
||||
}
|
||||
|
||||
|
||||
if(jvd.party_type && jvd.party) {
|
||||
out.filters.push([jvd.reference_type,
|
||||
out.filters.push([jvd.reference_type,
|
||||
(jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier"), "=", jvd.party]);
|
||||
}
|
||||
|
||||
@@ -244,6 +223,7 @@ cur_frm.cscript.company = function(doc, cdt, cdn) {
|
||||
}
|
||||
|
||||
cur_frm.cscript.posting_date = function(doc, cdt, cdn){
|
||||
cur_frm.posting_date = cur_frm.doc.posting_date;
|
||||
erpnext.get_fiscal_year(doc.company, doc.posting_date);
|
||||
}
|
||||
|
||||
@@ -347,17 +327,17 @@ frappe.ui.form.on("Journal Entry Account", {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
account: function(frm, dt, dn) {
|
||||
var d = locals[dt][dn];
|
||||
if(d.account) {
|
||||
if(!frm.doc.company) frappe.throw(__("Please select Company first"));
|
||||
if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first"));
|
||||
|
||||
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
|
||||
args: {
|
||||
account: d.account,
|
||||
account: d.account,
|
||||
date: frm.doc.posting_date,
|
||||
company: frm.doc.company,
|
||||
debit: flt(d.debit_in_account_currency),
|
||||
@@ -373,23 +353,23 @@ frappe.ui.form.on("Journal Entry Account", {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
debit_in_account_currency: function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
|
||||
credit_in_account_currency: function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
|
||||
debit: function(frm, dt, dn) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
|
||||
credit: function(frm, dt, dn) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
|
||||
exchange_rate: function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
|
||||
}
|
||||
@@ -399,41 +379,132 @@ frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
});
|
||||
|
||||
erpnext.journal_entry.set_debit_credit_in_company_currency = function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
|
||||
|
||||
var row = locals[cdt][cdn];
|
||||
|
||||
frappe.model.set_value(cdt, cdn, "debit",
|
||||
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
|
||||
frappe.model.set_value(cdt, cdn, "credit",
|
||||
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
|
||||
}
|
||||
$.extend(erpnext.journal_entry, {
|
||||
toggle_fields_based_on_currency: function(frm) {
|
||||
var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"];
|
||||
|
||||
erpnext.journal_entry.set_exchange_rate = function(frm, cdt, cdn) {
|
||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
var row = locals[cdt][cdn];
|
||||
|
||||
if(row.account_currency == company_currency || !frm.doc.multi_currency) {
|
||||
frappe.model.set_value(cdt, cdn, "exchange_rate", 1);
|
||||
} else if (!row.exchange_rate || row.account_type == "Bank") {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
|
||||
args: {
|
||||
account: row.account,
|
||||
account_currency: row.account_currency,
|
||||
company: frm.doc.company,
|
||||
reference_type: cstr(row.reference_type),
|
||||
reference_name: cstr(row.reference_name),
|
||||
debit: flt(row.debit_in_account_currency),
|
||||
credit: flt(row.credit_in_account_currency),
|
||||
exchange_rate: row.exchange_rate
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
|
||||
}
|
||||
}
|
||||
var grid = frm.get_field("accounts").grid;
|
||||
if(grid) grid.set_column_disp(fields, frm.doc.multi_currency);
|
||||
|
||||
// dynamic label
|
||||
var field_label_map = {
|
||||
"debit_in_account_currency": "Debit",
|
||||
"credit_in_account_currency": "Credit"
|
||||
};
|
||||
|
||||
$.each(field_label_map, function (fieldname, label) {
|
||||
var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
|
||||
df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
|
||||
})
|
||||
},
|
||||
|
||||
set_debit_credit_in_company_currency: function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
|
||||
|
||||
var row = locals[cdt][cdn];
|
||||
|
||||
frappe.model.set_value(cdt, cdn, "debit",
|
||||
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
|
||||
|
||||
frappe.model.set_value(cdt, cdn, "credit",
|
||||
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
|
||||
},
|
||||
|
||||
set_exchange_rate: function(frm, cdt, cdn) {
|
||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
var row = locals[cdt][cdn];
|
||||
|
||||
if(row.account_currency == company_currency || !frm.doc.multi_currency) {
|
||||
frappe.model.set_value(cdt, cdn, "exchange_rate", 1);
|
||||
} else if (!row.exchange_rate || row.account_type == "Bank") {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
|
||||
args: {
|
||||
account: row.account,
|
||||
account_currency: row.account_currency,
|
||||
company: frm.doc.company,
|
||||
reference_type: cstr(row.reference_type),
|
||||
reference_name: cstr(row.reference_name),
|
||||
debit: flt(row.debit_in_account_currency),
|
||||
credit: flt(row.credit_in_account_currency),
|
||||
exchange_rate: row.exchange_rate
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
quick_entry: function(frm) {
|
||||
var naming_series_options = frm.fields_dict.naming_series.df.options;
|
||||
var naming_series_default = frm.fields_dict.naming_series.df.default || naming_series_options.split("\n")[0];
|
||||
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Quick Journal Entry"),
|
||||
fields: [
|
||||
{fieldtype: "Currency", fieldname: "debit", label: __("Amount"), reqd: 1},
|
||||
{fieldtype: "Link", fieldname: "debit_account", label: __("Debit Account"), reqd: 1,
|
||||
options: "Account",
|
||||
get_query: function() {
|
||||
return erpnext.journal_entry.account_query(frm);
|
||||
}
|
||||
},
|
||||
{fieldtype: "Link", fieldname: "credit_account", label: __("Credit Account"), reqd: 1,
|
||||
options: "Account",
|
||||
get_query: function() {
|
||||
return erpnext.journal_entry.account_query(frm);
|
||||
}
|
||||
},
|
||||
{fieldtype: "Date", fieldname: "posting_date", label: __("Date"), reqd: 1,
|
||||
default: frm.doc.posting_date},
|
||||
{fieldtype: "Select", fieldname: "naming_series", label: __("Series"), reqd: 1,
|
||||
options: naming_series_options, default: naming_series_default},
|
||||
]
|
||||
});
|
||||
|
||||
dialog.set_primary_action(__("Save"), function() {
|
||||
var btn = this;
|
||||
var values = dialog.get_values();
|
||||
|
||||
frm.set_value("posting_date", values.posting_date);
|
||||
frm.set_value("naming_series", values.naming_series);
|
||||
|
||||
// clear table is used because there might've been an error while adding child
|
||||
// and cleanup didn't happen
|
||||
frm.clear_table("accounts");
|
||||
|
||||
// using grid.add_new_row() to add a row in UI as well as locals
|
||||
// this is required because triggers try to refresh the grid
|
||||
|
||||
var debit_row = frm.fields_dict.accounts.grid.add_new_row();
|
||||
frappe.model.set_value(debit_row.doctype, debit_row.name, "account", values.debit_account);
|
||||
frappe.model.set_value(debit_row.doctype, debit_row.name, "debit_in_account_currency", values.debit);
|
||||
|
||||
var credit_row = frm.fields_dict.accounts.grid.add_new_row();
|
||||
frappe.model.set_value(credit_row.doctype, credit_row.name, "account", values.credit_account);
|
||||
frappe.model.set_value(credit_row.doctype, credit_row.name, "credit_in_account_currency", values.debit);
|
||||
|
||||
frm.save();
|
||||
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
|
||||
account_query: function(frm) {
|
||||
var filters = {
|
||||
company: frm.doc.company,
|
||||
is_group: 0
|
||||
};
|
||||
if(!frm.doc.multi_currency) {
|
||||
$.extend(filters, {
|
||||
account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency
|
||||
});
|
||||
}
|
||||
return { filters: filters };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -498,14 +498,17 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
||||
if voucher_type=="Bank Entry":
|
||||
account = frappe.db.get_value("Company", company, "default_bank_account")
|
||||
if not account:
|
||||
account = frappe.db.get_value("Account", {"company": company, "account_type": "Bank", "is_group": 0})
|
||||
account = frappe.db.get_value("Account",
|
||||
{"company": company, "account_type": "Bank", "is_group": 0})
|
||||
elif voucher_type=="Cash Entry":
|
||||
account = frappe.db.get_value("Company", company, "default_cash_account")
|
||||
if not account:
|
||||
account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0})
|
||||
account = frappe.db.get_value("Account",
|
||||
{"company": company, "account_type": "Cash", "is_group": 0})
|
||||
|
||||
if account:
|
||||
account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1)
|
||||
account_details = frappe.db.get_value("Account", account,
|
||||
["account_currency", "account_type"], as_dict=1)
|
||||
return {
|
||||
"account": account,
|
||||
"balance": get_balance_on(account),
|
||||
@@ -814,11 +817,19 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
|
||||
return grid_values
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_exchange_rate(account, account_currency, company,
|
||||
def get_exchange_rate(account, account_currency=None, company=None,
|
||||
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
account_details = frappe.db.get_value("Account", account,
|
||||
["account_type", "root_type", "account_currency", "company"], as_dict=1)
|
||||
|
||||
if not company:
|
||||
company = account_details.company
|
||||
|
||||
if not account_currency:
|
||||
account_currency = account_details.account_currency
|
||||
|
||||
company_currency = get_company_currency(company)
|
||||
account_details = frappe.db.get_value("Account", account, ["account_type", "root_type"], as_dict=1)
|
||||
|
||||
if account_currency != company_currency:
|
||||
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
|
||||
|
||||
@@ -13,7 +13,7 @@ cur_frm.fields_dict['closing_account_head'].get_query = function(doc, cdt, cdn)
|
||||
return{
|
||||
filters:{
|
||||
"company": doc.company,
|
||||
"report_type": "Balance Sheet",
|
||||
"root_type": "Liability",
|
||||
"freeze_account": "No",
|
||||
"is_group": 0
|
||||
}
|
||||
|
||||
@@ -194,29 +194,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "coa_help",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "CoA Help",
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "HTML",
|
||||
"options": "<a href=\"#!Accounts Browser/Account\">To manage Account Head, click here</a>",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -250,7 +227,7 @@
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-10-02 07:39:00.056337",
|
||||
"modified": "2015-10-21 12:40:58.278256",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Period Closing Voucher",
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
class PeriodClosingVoucher(AccountsController):
|
||||
@@ -20,51 +21,75 @@ class PeriodClosingVoucher(AccountsController):
|
||||
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
|
||||
|
||||
def validate_account_head(self):
|
||||
if frappe.db.get_value("Account", self.closing_account_head, "report_type") \
|
||||
!= "Balance Sheet":
|
||||
frappe.throw(_("Closing Account {0} must be of type 'Liability'").format(self.closing_account_head))
|
||||
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
|
||||
|
||||
if closing_account_type != "Liability":
|
||||
frappe.throw(_("Closing Account {0} must be of type 'Liability'")
|
||||
.format(self.closing_account_head))
|
||||
|
||||
account_currency = get_account_currency(self.closing_account_head)
|
||||
company_currency = frappe.db.get_value("Company", self.company, "default_currency")
|
||||
if account_currency != company_currency:
|
||||
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
|
||||
|
||||
def validate_posting_date(self):
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
||||
|
||||
validate_fiscal_year(self.posting_date, self.fiscal_year, label=_("Posting Date"), doc=self)
|
||||
|
||||
self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year)[1]
|
||||
|
||||
pce = frappe.db.sql("""select name from `tabPeriod Closing Voucher`
|
||||
where posting_date > %s and fiscal_year = %s and docstatus = 1""",
|
||||
(self.posting_date, self.fiscal_year))
|
||||
if pce and pce[0][0]:
|
||||
frappe.throw(_("Another Period Closing Entry {0} has been made after {1}").format(pce[0][0], self.posting_date))
|
||||
|
||||
def get_pl_balances(self):
|
||||
"""Get balance for pl accounts"""
|
||||
return frappe.db.sql("""
|
||||
select t1.account, sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and ifnull(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
|
||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||
frappe.throw(_("Another Period Closing Entry {0} has been made after {1}")
|
||||
.format(pce[0][0], self.posting_date))
|
||||
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
net_pl_balance = 0
|
||||
pl_accounts = self.get_pl_balances()
|
||||
|
||||
for acc in pl_accounts:
|
||||
if flt(acc.balance):
|
||||
if flt(acc.balance_in_company_currency):
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": acc.account,
|
||||
"debit": abs(flt(acc.balance)) if flt(acc.balance) < 0 else 0,
|
||||
"credit": abs(flt(acc.balance)) if flt(acc.balance) > 0 else 0,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
||||
if flt(acc.balance_in_account_currency) < 0 else 0,
|
||||
"debit": abs(flt(acc.balance_in_company_currency)) \
|
||||
if flt(acc.balance_in_company_currency) < 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
||||
}))
|
||||
|
||||
net_pl_balance += flt(acc.balance)
|
||||
|
||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||
|
||||
if net_pl_balance:
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0
|
||||
}))
|
||||
|
||||
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def get_pl_balances(self):
|
||||
"""Get balance for pl accounts"""
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
t1.account, t2.account_currency, sum(ifnull(t1.debit_in_account_currency,0))-sum(ifnull(t1.credit_in_account_currency,0))
|
||||
as balance_in_account_currency,
|
||||
sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance_in_company_currency
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and ifnull(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
|
||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||
@@ -5,42 +5,74 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, today
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
|
||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
def test_closing_entry(self):
|
||||
year_start_date = get_fiscal_year(today())[1]
|
||||
|
||||
make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
|
||||
"_Test Cost Center - _TC", submit=True)
|
||||
|
||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC", 600, "_Test Cost Center - _TC", submit=True)
|
||||
|
||||
random_expense_account = frappe.db.sql("""
|
||||
select t1.account,
|
||||
sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance,
|
||||
sum(ifnull(t1.debit_in_account_currency,0))-sum(ifnull(t1.credit_in_account_currency,0)) \
|
||||
as balance_in_account_currency
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and ifnull(t2.root_type, '') = 'Expense'
|
||||
and t2.docstatus < 2 and t2.company = '_Test Company'
|
||||
and t1.posting_date between %s and %s
|
||||
group by t1.account
|
||||
having sum(ifnull(t1.debit,0)) > sum(ifnull(t1.credit,0))
|
||||
limit 1""", (year_start_date, today()), as_dict=True)
|
||||
|
||||
profit_or_loss = frappe.db.sql("""select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and ifnull(t2.report_type, '') = 'Profit and Loss'
|
||||
and t2.docstatus < 2 and t2.company = '_Test Company'
|
||||
and t1.posting_date between '2013-01-01' and '2013-12-31'""")
|
||||
and t1.posting_date between %s and %s""", (year_start_date, today()))
|
||||
|
||||
profit_or_loss = flt(profit_or_loss[0][0]) if profit_or_loss else 0
|
||||
|
||||
pcv = self.make_period_closing_voucher()
|
||||
|
||||
gle_value = frappe.db.sql("""select ifnull(debit, 0) - ifnull(credit, 0)
|
||||
# Check value for closing account
|
||||
gle_amount_for_closing_account = frappe.db.sql("""select ifnull(debit, 0) - ifnull(credit, 0)
|
||||
from `tabGL Entry` where voucher_type='Period Closing Voucher' and voucher_no=%s
|
||||
and account = '_Test Account Reserves and Surplus - _TC'""", pcv.name)
|
||||
|
||||
gle_value = flt(gle_value[0][0]) if gle_value else 0
|
||||
gle_amount_for_closing_account = flt(gle_amount_for_closing_account[0][0]) \
|
||||
if gle_amount_for_closing_account else 0
|
||||
|
||||
self.assertEqual(gle_value, profit_or_loss)
|
||||
self.assertEqual(gle_amount_for_closing_account, profit_or_loss)
|
||||
|
||||
if random_expense_account:
|
||||
# Check posted value for teh above random_expense_account
|
||||
gle_for_random_expense_account = frappe.db.sql("""
|
||||
select ifnull(debit, 0) - ifnull(credit, 0) as amount,
|
||||
ifnull(debit_in_account_currency, 0) - ifnull(credit_in_account_currency, 0)
|
||||
as amount_in_account_currency
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Period Closing Voucher' and voucher_no=%s and account =%s""",
|
||||
(pcv.name, random_expense_account[0].account), as_dict=True)
|
||||
|
||||
self.assertEqual(gle_for_random_expense_account[0].amount, -1*random_expense_account[0].balance)
|
||||
self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
|
||||
-1*random_expense_account[0].balance_in_account_currency)
|
||||
|
||||
def make_period_closing_voucher(self):
|
||||
pcv = frappe.get_doc({
|
||||
"doctype": "Period Closing Voucher",
|
||||
"closing_account_head": "_Test Account Reserves and Surplus - _TC",
|
||||
"company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"posting_date": "2013-12-31",
|
||||
"fiscal_year": get_fiscal_year(today())[0],
|
||||
"posting_date": today(),
|
||||
"remarks": "test"
|
||||
})
|
||||
pcv.insert()
|
||||
|
||||
@@ -317,23 +317,22 @@ class PurchaseInvoice(BuyingController):
|
||||
if auto_accounting_for_stock and self.is_opening == "No" and \
|
||||
item.item_code in stock_items and item.item_tax_amount:
|
||||
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
||||
negative_expense_booked_in_pi = None
|
||||
if item.purchase_receipt:
|
||||
negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabGL Entry`
|
||||
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
||||
(item.purchase_receipt, expenses_included_in_valuation))
|
||||
|
||||
if not negative_expense_booked_in_pi:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": stock_received_but_not_billed,
|
||||
"against": self.supplier,
|
||||
"debit": flt(item.item_tax_amount, self.precision("item_tax_amount", item)),
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
})
|
||||
)
|
||||
if not negative_expense_booked_in_pr:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": stock_received_but_not_billed,
|
||||
"against": self.supplier,
|
||||
"debit": flt(item.item_tax_amount, self.precision("item_tax_amount", item)),
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
})
|
||||
)
|
||||
|
||||
negative_expense_to_be_booked += flt(item.item_tax_amount, self.precision("item_tax_amount", item))
|
||||
negative_expense_to_be_booked += flt(item.item_tax_amount, self.precision("item_tax_amount", item))
|
||||
|
||||
if self.is_opening == "No" and negative_expense_to_be_booked and valuation_tax:
|
||||
# credit valuation tax amount in "Expenses Included In Valuation"
|
||||
|
||||
@@ -49,25 +49,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi = frappe.copy_doc(test_records[1])
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.name, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = dict((d[0], d) for d in [
|
||||
["_Test Payable - _TC", 0, 720],
|
||||
["Stock Received But Not Billed - _TC", 750.0, 0],
|
||||
["Expenses Included In Valuation - _TC", 0.0, 250.0],
|
||||
["_Test Account Shipping Charges - _TC", 100.0, 0],
|
||||
["_Test Account VAT - _TC", 120.0, 0],
|
||||
])
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals(expected_values[gle.account][0], gle.account)
|
||||
self.assertEquals(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||
|
||||
|
||||
self.check_gle_for_pi(pi.name)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def test_gl_entries_with_auto_accounting_for_stock_against_pr(self):
|
||||
@@ -83,9 +67,14 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
|
||||
self.check_gle_for_pi(pi.name)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def check_gle_for_pi(self, pi):
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.name, as_dict=1)
|
||||
order by account asc""", pi, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = dict((d[0], d) for d in [
|
||||
@@ -100,8 +89,6 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEquals(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def test_gl_entries_with_aia_for_non_stock_items(self):
|
||||
set_perpetual_inventory()
|
||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
||||
|
||||
@@ -160,6 +160,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
me.set_dynamic_labels();
|
||||
me.calculate_taxes_and_totals();
|
||||
if(callback_fn) callback_fn();
|
||||
frappe.after_ajax(function() {
|
||||
cur_frm.doc.__missing_values_set = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -109,7 +109,8 @@ def get_party_details(party, party_type, args=None):
|
||||
def get_tax_template(posting_date, args):
|
||||
"""Get matching tax rule"""
|
||||
args = frappe._dict(args)
|
||||
conditions = []
|
||||
conditions = ["""(from_date is null or from_date = '' or from_date <= '{0}')
|
||||
and (to_date is null or to_date = '' or to_date >= '{0}')""".format(posting_date)]
|
||||
|
||||
for key, value in args.iteritems():
|
||||
if key in "use_for_shopping_cart":
|
||||
@@ -117,16 +118,16 @@ def get_tax_template(posting_date, args):
|
||||
else:
|
||||
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
||||
|
||||
matching = frappe.db.sql("""select * from `tabTax Rule`
|
||||
tax_rule = frappe.db.sql("""select * from `tabTax Rule`
|
||||
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
||||
|
||||
if not matching:
|
||||
if not tax_rule:
|
||||
return None
|
||||
|
||||
for rule in matching:
|
||||
for rule in tax_rule:
|
||||
rule.no_of_keys_matched = 0
|
||||
for key in args:
|
||||
if rule.get(key): rule.no_of_keys_matched += 1
|
||||
|
||||
rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
|
||||
rule = sorted(tax_rule, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
|
||||
return rule.sales_tax_template or rule.purchase_tax_template
|
||||
|
||||
@@ -209,13 +209,12 @@ erpnext.AccountsChart = Class.extend({
|
||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
||||
options: ['', 'Bank', 'Cash', 'Warehouse', 'Receivable', 'Payable',
|
||||
'Equity', 'Cost of Goods Sold', 'Fixed Asset', 'Expense Account',
|
||||
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
|
||||
options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'),
|
||||
description: __("Optional. This setting will be used to filter in various transactions.") },
|
||||
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
|
||||
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
|
||||
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"}
|
||||
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
|
||||
description: __("Optional. Sets company's default currency, if not specified.")}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -201,11 +201,14 @@ def get_party_gle_currency(party_type, party, company):
|
||||
|
||||
return existing_gle_currency[0][0] if existing_gle_currency else None
|
||||
|
||||
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator)
|
||||
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator,
|
||||
regenerate_if_none=True)
|
||||
|
||||
def validate_party_gle_currency(party_type, party, company):
|
||||
def validate_party_gle_currency(party_type, party, company, party_account_currency=None):
|
||||
"""Validate party account currency with existing GL Entry's currency"""
|
||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||
if not party_account_currency:
|
||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||
|
||||
existing_gle_currency = get_party_gle_currency(party_type, party, company)
|
||||
|
||||
if existing_gle_currency and party_account_currency != existing_gle_currency:
|
||||
@@ -221,10 +224,10 @@ def validate_party_accounts(doc):
|
||||
.format(doc.doctype, doc.name), DuplicatePartyAccountError)
|
||||
else:
|
||||
companies.append(account.company)
|
||||
|
||||
|
||||
party_account_currency = frappe.db.get_value("Account", account.account, "account_currency")
|
||||
existing_gle_currency = get_party_gle_currency(doc.doctype, doc.name, account.company)
|
||||
|
||||
|
||||
if existing_gle_currency and party_account_currency != existing_gle_currency:
|
||||
frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company))
|
||||
|
||||
|
||||
2
erpnext/change_log/v6/v6_6_0.md
Normal file
2
erpnext/change_log/v6/v6_6_0.md
Normal file
@@ -0,0 +1,2 @@
|
||||
- **Quick Entry dialog for Journal Entry**: Just enter Amount, Accounts, Date and save!
|
||||
- Period Closing Voucher as per multi-currency accounting
|
||||
@@ -10,7 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_ac
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
from erpnext.controllers.sales_and_purchase_return import validate_return
|
||||
from erpnext.accounts.party import get_party_account_currency, validate_party_gle_currency
|
||||
from erpnext.accounts.party import get_party_account_currency
|
||||
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
|
||||
|
||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||
@@ -221,7 +221,7 @@ class AccountsController(TransactionBase):
|
||||
if not account_currency:
|
||||
account_currency = get_account_currency(gl_dict.account)
|
||||
|
||||
if self.doctype != "Journal Entry":
|
||||
if self.doctype not in ["Journal Entry", "Period Closing Voucher"]:
|
||||
self.validate_account_currency(gl_dict.account, account_currency)
|
||||
self.set_balance_in_account_currency(gl_dict, account_currency)
|
||||
|
||||
@@ -435,6 +435,8 @@ class AccountsController(TransactionBase):
|
||||
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||
.format(party_type, party, party_account_currency), InvalidCurrency)
|
||||
|
||||
# Note: not validating with gle account because we don't have the account at quotation / sales order level and we shouldn't stop someone from creating a sales invoice if sales order is already created
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
return frappe.db.get_value("Account", account_head, "tax_rate")
|
||||
|
||||
@@ -29,7 +29,7 @@ blogs.
|
||||
"""
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "6.5.2"
|
||||
app_version = "6.6.5"
|
||||
github_link = "https://github.com/frappe/erpnext"
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
@@ -18,24 +18,25 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
||||
jv.voucher_type = 'Bank Entry';
|
||||
jv.company = cur_frm.doc.company;
|
||||
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
|
||||
jv.fiscal_year = cur_frm.doc.fiscal_year;
|
||||
var expense = cur_frm.doc.expenses || [];
|
||||
for(var i = 0; i < expense.length; i++){
|
||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||
d1.debit = expense[i].sanctioned_amount;
|
||||
d1.account = expense[i].default_account;
|
||||
d1.debit_in_account_currency = expense[i].sanctioned_amount;
|
||||
d1.reference_type = cur_frm.doc.doctype;
|
||||
d1.reference_name = cur_frm.doc.name;
|
||||
}
|
||||
|
||||
// credit to bank
|
||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||
d1.credit = cur_frm.doc.total_sanctioned_amount;
|
||||
d1.credit_in_account_currency = cur_frm.doc.total_sanctioned_amount;
|
||||
d1.reference_type = cur_frm.doc.doctype;
|
||||
d1.reference_name = cur_frm.doc.name;
|
||||
if(r.message) {
|
||||
d1.account = r.message.account;
|
||||
d1.balance = r.message.balance;
|
||||
d1.account_currency = r.message.account_currency;
|
||||
d1.account_type = r.message.account_type;
|
||||
}
|
||||
|
||||
loaddoc('Journal Entry', jv.name);
|
||||
|
||||
@@ -2,114 +2,118 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.add_fetch('employee','employee_name','employee_name');
|
||||
cur_frm.add_fetch('employee','company','company');
|
||||
|
||||
frappe.ui.form.on("Leave Application", "leave_approver", function(frm) {
|
||||
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
|
||||
});
|
||||
frappe.ui.form.on("Leave Application", {
|
||||
onload: function(frm) {
|
||||
if (!frm.doc.posting_date) {
|
||||
frm.set_value("posting_date", get_today());
|
||||
}
|
||||
|
||||
cur_frm.cscript.onload = function(doc, dt, dn) {
|
||||
if(!doc.posting_date)
|
||||
set_multiple(dt,dn,{posting_date:get_today()});
|
||||
if(doc.__islocal) {
|
||||
cur_frm.set_value("status", "Open");
|
||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
||||
}
|
||||
frm.set_query("leave_approver", function() {
|
||||
return {
|
||||
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
||||
filters: {
|
||||
employee: frm.doc.employee
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
cur_frm.set_query("leave_approver", function() {
|
||||
return {
|
||||
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
||||
filters: {
|
||||
employee: cur_frm.doc.employee
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("employee", erpnext.queries.employee);
|
||||
|
||||
cur_frm.cscript.get_leave_balance(cur_frm.doc);
|
||||
}
|
||||
},
|
||||
|
||||
cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
if(doc.__islocal) {
|
||||
cur_frm.set_value("status", "Open")
|
||||
}
|
||||
cur_frm.set_intro("");
|
||||
if(doc.__islocal && !in_list(user_roles, "HR User")) {
|
||||
cur_frm.set_intro(__("Fill the form and save it"))
|
||||
} else {
|
||||
if(doc.docstatus==0 && doc.status=="Open") {
|
||||
if(user==doc.leave_approver) {
|
||||
cur_frm.set_intro(__("You are the Leave Approver for this record. Please Update the 'Status' and Save"));
|
||||
cur_frm.toggle_enable("status", true);
|
||||
} else {
|
||||
cur_frm.set_intro(__("This Leave Application is pending approval. Only the Leave Approver can update status."))
|
||||
cur_frm.toggle_enable("status", false);
|
||||
refresh: function(frm) {
|
||||
if (frm.is_new()) {
|
||||
frm.set_value("status", "Open");
|
||||
frm.trigger("calculate_total_days");
|
||||
}
|
||||
|
||||
frm.set_intro("");
|
||||
if (frm.is_new() && !in_list(user_roles, "HR User")) {
|
||||
frm.set_intro(__("Fill the form and save it"));
|
||||
} else {
|
||||
if(frm.doc.docstatus==0 && frm.doc.status=="Open") {
|
||||
if(user==frm.doc.leave_approver) {
|
||||
frm.set_intro(__("You are the Leave Approver for this record. Please Update the 'Status' and Save"));
|
||||
frm.toggle_enable("status", true);
|
||||
} else {
|
||||
frm.set_intro(__("This Leave Application is pending approval. Only the Leave Approver can update status."))
|
||||
frm.toggle_enable("status", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
cur_frm.cscript.employee = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
leave_approver: function(frm) {
|
||||
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
|
||||
},
|
||||
|
||||
cur_frm.cscript.fiscal_year = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
employee: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
},
|
||||
|
||||
cur_frm.cscript.leave_type = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
fiscal_year: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
},
|
||||
|
||||
cur_frm.cscript.half_day = function(doc, dt, dn) {
|
||||
if(doc.from_date) {
|
||||
set_multiple(dt,dn,{to_date:doc.from_date});
|
||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
||||
}
|
||||
}
|
||||
leave_type: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
},
|
||||
|
||||
cur_frm.cscript.from_date = function(doc, dt, dn) {
|
||||
if(cint(doc.half_day) == 1){
|
||||
set_multiple(dt,dn,{to_date:doc.from_date});
|
||||
}
|
||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
||||
}
|
||||
half_day: function(frm) {
|
||||
if (frm.doc.from_date) {
|
||||
frm.set_value("to_date", frm.doc.from_date);
|
||||
frm.trigger("calculate_total_days");
|
||||
}
|
||||
},
|
||||
|
||||
cur_frm.cscript.to_date = function(doc, dt, dn) {
|
||||
if(cint(doc.half_day) == 1 && cstr(doc.from_date) && doc.from_date != doc.to_date){
|
||||
msgprint(__("To Date should be same as From Date for Half Day leave"));
|
||||
set_multiple(dt,dn,{to_date:doc.from_date});
|
||||
}
|
||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
||||
}
|
||||
from_date: function(frm) {
|
||||
if (cint(frm.doc.half_day)==1) {
|
||||
frm.set_value("to_date", frm.doc.from_date);
|
||||
}
|
||||
frm.trigger("calculate_total_days");
|
||||
},
|
||||
|
||||
cur_frm.cscript.get_leave_balance = function(doc, dt, dn) {
|
||||
if(doc.docstatus==0 && doc.employee && doc.leave_type && doc.fiscal_year) {
|
||||
return cur_frm.call({
|
||||
method: "get_leave_balance",
|
||||
args: {
|
||||
employee: doc.employee,
|
||||
fiscal_year: doc.fiscal_year,
|
||||
leave_type: doc.leave_type
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
to_date: function(frm) {
|
||||
if (cint(frm.doc.half_day)==1 && cstr(frm.doc.from_date) && frm.doc.from_date != frm.doc.to_date) {
|
||||
msgprint(__("To Date should be same as From Date for Half Day leave"));
|
||||
frm.set_value("to_date", frm.doc.from_date);
|
||||
}
|
||||
|
||||
cur_frm.cscript.calculate_total_days = function(doc, dt, dn) {
|
||||
if(doc.from_date && doc.to_date){
|
||||
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
|
||||
else{
|
||||
// server call is done to include holidays in leave days calculations
|
||||
return frappe.call({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
|
||||
args: {leave_app: doc},
|
||||
callback: function(response) {
|
||||
if (response && response.message) {
|
||||
cur_frm.set_value('total_leave_days', response.message.total_leave_days);
|
||||
}
|
||||
frm.trigger("calculate_total_days");
|
||||
},
|
||||
|
||||
get_leave_balance: function(frm) {
|
||||
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.fiscal_year) {
|
||||
return frm.call({
|
||||
method: "get_leave_balance",
|
||||
args: {
|
||||
employee: frm.doc.employee,
|
||||
fiscal_year: frm.doc.fiscal_year,
|
||||
leave_type: frm.doc.leave_type
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
cur_frm.fields_dict.employee.get_query = erpnext.queries.employee;
|
||||
calculate_total_days: function(frm) {
|
||||
if(frm.doc.from_date && frm.doc.to_date) {
|
||||
if (cint(frm.doc.half_day)==1) {
|
||||
frm.set_value("total_leave_days", 0.5);
|
||||
} else {
|
||||
// server call is done to include holidays in leave days calculations
|
||||
return frappe.call({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
|
||||
args: { leave_app: frm.doc },
|
||||
callback: function(response) {
|
||||
if (response && response.message) {
|
||||
frm.set_value('total_leave_days', response.message.total_leave_days);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -224,8 +224,7 @@ erpnext.patches.v6_4.email_digest_update
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory")
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
|
||||
|
||||
erpnext.patches.v6_4.set_user_in_contact
|
||||
erpnext.patches.v6_4.make_image_thumbnail
|
||||
|
||||
erpnext.patches.v6_4.make_image_thumbnail #2015-10-20
|
||||
erpnext.patches.v6_5.show_in_website_for_template_item
|
||||
erpnext.patches.v6_4.fix_expense_included_in_valuation
|
||||
|
||||
74
erpnext/patches/v6_4/fix_expense_included_in_valuation.py
Normal file
74
erpnext/patches/v6_4/fix_expense_included_in_valuation.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import cstr
|
||||
|
||||
def execute():
|
||||
for company in frappe.db.sql("select name, expenses_included_in_valuation from tabCompany", as_dict=1):
|
||||
frozen_date = get_frozen_date(company.name, company.expenses_included_in_valuation)
|
||||
|
||||
# Purchase Invoices after frozen date
|
||||
# which are not against Receipt, but valuation related tax is there
|
||||
pi_list = frappe.db.sql("""
|
||||
select distinct pi.name
|
||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
||||
where
|
||||
pi.name = pi_item.parent
|
||||
and pi.company = %s
|
||||
and pi.posting_date > %s
|
||||
and pi.docstatus = 1
|
||||
and pi.is_opening = 'No'
|
||||
and (pi_item.item_tax_amount is not null and pi_item.item_tax_amount > 0)
|
||||
and (pi_item.purchase_receipt is null or pi_item.purchase_receipt = '')
|
||||
and (pi_item.item_code is not null and pi_item.item_code != '')
|
||||
and exists(select name from `tabItem` where name=pi_item.item_code and is_stock_item=1)
|
||||
""", (company.name, frozen_date), as_dict=1)
|
||||
|
||||
for pi in pi_list:
|
||||
# Check whether gle exists for Expenses Included in Valuation account against the PI
|
||||
gle_for_expenses_included_in_valuation = frappe.db.sql("""select name from `tabGL Entry`
|
||||
where voucher_type='Purchase Invoice' and voucher_no=%s and account=%s""",
|
||||
(pi.name, company.expenses_included_in_valuation))
|
||||
|
||||
if gle_for_expenses_included_in_valuation:
|
||||
print pi.name
|
||||
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type='Purchase Invoice' and voucher_no=%s""", pi.name)
|
||||
|
||||
purchase_invoice = frappe.get_doc("Purchase Invoice", pi.name)
|
||||
|
||||
# some old entries have missing expense accounts
|
||||
if purchase_invoice.against_expense_account:
|
||||
expense_account = purchase_invoice.against_expense_account.split(",")
|
||||
if len(expense_account) == 1:
|
||||
expense_account = expense_account[0]
|
||||
for item in purchase_invoice.items:
|
||||
if not item.expense_account:
|
||||
item.db_set("expense_account", expense_account, update_modified=False)
|
||||
|
||||
purchase_invoice.make_gl_entries()
|
||||
|
||||
def get_frozen_date(company, account):
|
||||
# Accounting frozen upto
|
||||
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||
|
||||
# Last adjustment entry to correct Expenses Included in Valuation account balance
|
||||
last_adjustment_entry = frappe.db.sql("""select posting_date from `tabGL Entry`
|
||||
where account=%s and company=%s and voucher_type = 'Journal Entry'
|
||||
order by posting_date desc limit 1""", (account, company))
|
||||
|
||||
last_adjustment_date = cstr(last_adjustment_entry[0][0]) if last_adjustment_entry else None
|
||||
|
||||
# Last period closing voucher
|
||||
last_closing_entry = frappe.db.sql("""select posting_date from `tabGL Entry`
|
||||
where company=%s and voucher_type = 'Period Closing Voucher'
|
||||
order by posting_date desc limit 1""", company)
|
||||
|
||||
last_closing_date = cstr(last_closing_entry[0][0]) if last_closing_entry else None
|
||||
|
||||
frozen_date = max([accounts_frozen_upto, last_adjustment_date, last_closing_date])
|
||||
|
||||
return frozen_date or '1900-01-01'
|
||||
@@ -3,8 +3,8 @@ import frappe
|
||||
def execute():
|
||||
frappe.reload_doctype("File")
|
||||
frappe.reload_doctype("Item")
|
||||
for item in frappe.get_all("Item", fields=("name", "website_image")):
|
||||
if item.website_image:
|
||||
for item in frappe.get_all("Item", fields=("name", "website_image", "thumbnail")):
|
||||
if item.website_image and not item.thumbnail:
|
||||
item_doc = frappe.get_doc("Item", item.name)
|
||||
try:
|
||||
item_doc.make_thumbnail()
|
||||
|
||||
@@ -12,6 +12,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
var today = get_today(),
|
||||
currency = frappe.defaults.get_user_default("currency");
|
||||
|
||||
$.each(["posting_date", "transaction_date"], function(i, fieldname) {
|
||||
if (me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname] && me.frm[fieldname]) {
|
||||
me.frm.set_value(fieldname, me.frm[fieldname]);
|
||||
}
|
||||
});
|
||||
|
||||
$.each({
|
||||
posting_date: today,
|
||||
transaction_date: today,
|
||||
@@ -277,12 +283,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
transaction_date: function() {
|
||||
if (this.frm.doc.transaction_date) {
|
||||
this.frm.transaction_date = this.frm.doc.transaction_date;
|
||||
}
|
||||
|
||||
erpnext.get_fiscal_year(this.frm.doc.company, this.frm.doc.transaction_date);
|
||||
},
|
||||
|
||||
posting_date: function() {
|
||||
var me = this;
|
||||
if (this.frm.doc.posting_date) {
|
||||
this.frm.posting_date = this.frm.doc.posting_date;
|
||||
|
||||
if ((this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.customer) ||
|
||||
(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.supplier)) {
|
||||
return frappe.call({
|
||||
@@ -429,7 +441,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
setup_field_label_map(["total", "net_total", "total_taxes_and_charges", "discount_amount",
|
||||
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted",
|
||||
"rounded_total", "in_words", "paid_amount", "write_off_amount"], this.frm.doc.currency);
|
||||
|
||||
|
||||
setup_field_label_map(["outstanding_amount", "total_advance"], this.frm.doc.party_account_currency);
|
||||
|
||||
cur_frm.set_df_property("conversion_rate", "description", "1 " + this.frm.doc.currency
|
||||
|
||||
@@ -22,11 +22,16 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||
}
|
||||
|
||||
if (args) {
|
||||
args.posting_date = frm.doc.transaction_date;
|
||||
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
|
||||
}
|
||||
}
|
||||
if(!args) return;
|
||||
|
||||
|
||||
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
||||
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date",
|
||||
args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return;
|
||||
}
|
||||
|
||||
args.currency = frm.doc.currency;
|
||||
args.company = frm.doc.company;
|
||||
args.doctype = frm.doc.doctype;
|
||||
@@ -64,6 +69,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
|
||||
if(r.message){
|
||||
frm.set_value(display_field, r.message)
|
||||
}
|
||||
|
||||
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
||||
if(!erpnext.utils.validate_mandatory(frm, "Customer/Supplier",
|
||||
frm.doc.customer || frm.doc.supplier, address_field)) return;
|
||||
|
||||
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date",
|
||||
frm.doc.posting_date || frm.doc.transaction_date, address_field)) return;
|
||||
} else return;
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.party.set_taxes",
|
||||
args: {
|
||||
@@ -99,3 +113,13 @@ erpnext.utils.get_contact_details = function(frm) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
|
||||
if(!value) {
|
||||
frm.doc[trigger_on] = "";
|
||||
refresh_field(trigger_on);
|
||||
frappe.msgprint(__("Please enter {0} first", [label]));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -50,14 +50,6 @@ class InstallationNote(TransactionBase):
|
||||
if not frappe.db.exists("Serial No", x):
|
||||
frappe.throw(_("Serial No {0} does not exist").format(x))
|
||||
|
||||
def is_serial_no_installed(self,cur_s_no,item_code):
|
||||
for x in cur_s_no:
|
||||
status = frappe.db.sql("select status from `tabSerial No` where name = %s", x)
|
||||
status = status and status[0][0] or ''
|
||||
|
||||
if status == 'Installed':
|
||||
frappe.throw(_("Item {0} with Serial No {1} is already installed").format(item_code, x))
|
||||
|
||||
def get_prevdoc_serial_no(self, prevdoc_detail_docname):
|
||||
serial_nos = frappe.db.get_value("Delivery Note Item",
|
||||
prevdoc_detail_docname, "serial_no")
|
||||
@@ -80,7 +72,6 @@ class InstallationNote(TransactionBase):
|
||||
if prevdoc_s_no:
|
||||
self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
|
||||
|
||||
self.is_serial_no_installed(sr_list, d.item_code)
|
||||
|
||||
def validate_installation_date(self):
|
||||
for d in self.get('items'):
|
||||
@@ -102,11 +93,5 @@ class InstallationNote(TransactionBase):
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
|
||||
def on_cancel(self):
|
||||
for d in self.get('items'):
|
||||
if d.serial_no:
|
||||
d.serial_no = d.serial_no.replace(",", "\n")
|
||||
for sr_no in d.serial_no.split("\n"):
|
||||
frappe.db.set_value("Serial No", sr_no, "status", "Delivered")
|
||||
|
||||
self.update_prevdoc_status()
|
||||
frappe.db.set(self, 'status', 'Cancelled')
|
||||
|
||||
@@ -22,8 +22,10 @@ def delete_company_transactions(company_name):
|
||||
|
||||
for doctype in frappe.db.sql_list("""select parent from
|
||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account", "Employee"):
|
||||
delete_for_doctype(doctype, company_name)
|
||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail",
|
||||
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
||||
"Purchase Taxes and Charges Template", "POS Profile"):
|
||||
delete_for_doctype(doctype, company_name)
|
||||
|
||||
# Clear notification counts
|
||||
clear_notifications()
|
||||
|
||||
@@ -139,7 +139,7 @@ class EmailDigest(Document):
|
||||
|
||||
for i, e in enumerate(events):
|
||||
e.starts_on_label = format_time(e.starts_on)
|
||||
e.ends_on_label = format_time(e.ends_on)
|
||||
e.ends_on_label = format_time(e.ends_on) if e.ends_on else None
|
||||
e.date = formatdate(e.starts)
|
||||
e.link = get_url_to_form("Event", e.name)
|
||||
|
||||
@@ -168,7 +168,7 @@ class EmailDigest(Document):
|
||||
for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
|
||||
"invoiced_amount", "payables", "bank_balance"):
|
||||
if self.get(key):
|
||||
cache_key = "email_digest:card:" + key
|
||||
cache_key = "email_digest:card:{0}:{1}".format(self.company, key)
|
||||
card = cache.get(cache_key)
|
||||
|
||||
if card:
|
||||
@@ -346,7 +346,7 @@ class EmailDigest(Document):
|
||||
self.get_next_sending()
|
||||
|
||||
def fmt_money(self, value):
|
||||
return fmt_money(value, currency = self.currency)
|
||||
return fmt_money(abs(value), currency = self.currency)
|
||||
|
||||
def send():
|
||||
now_date = now_datetime().date()
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
<span style="{{ label_css }}">
|
||||
{% if e.all_day %}
|
||||
{{ _("All Day") }}
|
||||
{% elif (not e.ends_on_label or e.starts_on_label == e.ends_on_label)%}
|
||||
{{ e.starts_on_label }}
|
||||
{% else %}
|
||||
{{ e.starts_on_label }} - {{ e.ends_on_label }}
|
||||
{% endif %}
|
||||
|
||||
@@ -374,6 +374,7 @@ def create_items(args):
|
||||
is_sales_item = args.get("is_sales_item_" + str(i))
|
||||
is_purchase_item = args.get("is_purchase_item_" + str(i))
|
||||
is_stock_item = item_group!=_("Services")
|
||||
is_pro_applicable = item_group!=_("Services")
|
||||
default_warehouse = ""
|
||||
if is_stock_item:
|
||||
default_warehouse = frappe.db.get_value("Warehouse", filters={
|
||||
@@ -391,6 +392,7 @@ def create_items(args):
|
||||
"is_purchase_item": 1 if is_purchase_item else 0,
|
||||
"show_in_website": 1,
|
||||
"is_stock_item": is_stock_item and 1 or 0,
|
||||
"is_pro_applicable": is_pro_applicable and 1 or 0,
|
||||
"item_group": item_group,
|
||||
"stock_uom": args.get("item_uom_" + str(i)),
|
||||
"default_warehouse": default_warehouse
|
||||
|
||||
@@ -292,7 +292,7 @@ def get_customer(user=None):
|
||||
"customer_group": get_shopping_cart_settings().default_customer_group,
|
||||
"territory": get_root_of("Territory")
|
||||
})
|
||||
customer.ignore_mandatory = True
|
||||
customer.flags.ignore_mandatory = True
|
||||
customer.insert(ignore_permissions=True)
|
||||
|
||||
contact = frappe.new_doc("Contact")
|
||||
@@ -301,7 +301,7 @@ def get_customer(user=None):
|
||||
"first_name": fullname,
|
||||
"email_id": user
|
||||
})
|
||||
contact.ignore_mandatory = True
|
||||
contact.flags.ignore_mandatory = True
|
||||
contact.insert(ignore_permissions=True)
|
||||
|
||||
return customer
|
||||
|
||||
@@ -735,7 +735,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Warehouse",
|
||||
"label": "From Warehouse",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
@@ -755,13 +755,15 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Target Warehouse",
|
||||
"label": "To Warehouse (Optional)",
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
@@ -831,7 +833,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Available Qty at Warehouse",
|
||||
"label": "Available Qty at From Warehouse",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "actual_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
@@ -857,7 +859,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Available Batch Qty at Warehouse",
|
||||
"label": "Available Batch Qty at From Warehouse",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
@@ -1160,7 +1162,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-10-19 03:04:50.887288",
|
||||
"modified": "2015-10-26 02:19:18.053222",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
import urllib
|
||||
import itertools
|
||||
from frappe import msgprint, _
|
||||
from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
@@ -98,6 +99,8 @@ class Item(WebsiteGenerator):
|
||||
})
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
# cleanup
|
||||
frappe.local.message_log.pop()
|
||||
|
||||
# for CSV import
|
||||
if not file_doc:
|
||||
@@ -128,6 +131,8 @@ class Item(WebsiteGenerator):
|
||||
|
||||
self.set_attribute_context(context)
|
||||
|
||||
self.set_disabled_attributes(context)
|
||||
|
||||
context.parents = self.get_parents(context)
|
||||
|
||||
return context
|
||||
@@ -142,20 +147,21 @@ class Item(WebsiteGenerator):
|
||||
filters={"variant_of": self.name, "show_in_website": 1}, order_by="name asc")
|
||||
|
||||
variant = frappe.form_dict.variant
|
||||
if not variant:
|
||||
if not variant and context.variants:
|
||||
# the case when the item is opened for the first time from its list
|
||||
variant = context.variants[0]
|
||||
|
||||
context.variant = frappe.get_doc("Item", variant)
|
||||
if variant:
|
||||
context.variant = frappe.get_doc("Item", variant)
|
||||
|
||||
for fieldname in ("website_image", "web_long_description", "description",
|
||||
"website_specifications"):
|
||||
if context.variant.get(fieldname):
|
||||
value = context.variant.get(fieldname)
|
||||
if isinstance(value, list):
|
||||
value = [d.as_dict() for d in value]
|
||||
for fieldname in ("website_image", "web_long_description", "description",
|
||||
"website_specifications"):
|
||||
if context.variant.get(fieldname):
|
||||
value = context.variant.get(fieldname)
|
||||
if isinstance(value, list):
|
||||
value = [d.as_dict() for d in value]
|
||||
|
||||
context[fieldname] = value
|
||||
context[fieldname] = value
|
||||
|
||||
if self.slideshow:
|
||||
if context.variant and context.variant.slideshow:
|
||||
@@ -186,15 +192,63 @@ class Item(WebsiteGenerator):
|
||||
for attr in self.attributes:
|
||||
values = context.attribute_values.setdefault(attr.attribute, [])
|
||||
|
||||
# get list of values defined (for sequence)
|
||||
for attr_value in frappe.db.get_all("Item Attribute Value",
|
||||
fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
|
||||
if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
|
||||
for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
|
||||
values.append(val)
|
||||
|
||||
if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
|
||||
values.append(attr_value.attribute_value)
|
||||
else:
|
||||
# get list of values defined (for sequence)
|
||||
for attr_value in frappe.db.get_all("Item Attribute Value",
|
||||
fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
|
||||
|
||||
if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
|
||||
values.append(attr_value.attribute_value)
|
||||
|
||||
context.variant_info = json.dumps(context.variants)
|
||||
|
||||
def set_disabled_attributes(self, context):
|
||||
"""Disable selection options of attribute combinations that do not result in a variant"""
|
||||
if not self.attributes:
|
||||
return
|
||||
|
||||
context.disabled_attributes = {}
|
||||
attributes = [attr.attribute for attr in self.attributes]
|
||||
|
||||
def find_variant(combination):
|
||||
for variant in context.variants:
|
||||
if len(variant.attributes) < len(attributes):
|
||||
continue
|
||||
|
||||
if "combination" not in variant:
|
||||
ref_combination = []
|
||||
|
||||
for attr in variant.attributes:
|
||||
idx = attributes.index(attr.attribute)
|
||||
ref_combination.insert(idx, attr.attribute_value)
|
||||
|
||||
variant["combination"] = ref_combination
|
||||
|
||||
if not (set(combination) - set(variant["combination"])):
|
||||
# check if the combination is a subset of a variant combination
|
||||
# eg. [Blue, 0.5] is a possible combination if exists [Blue, Large, 0.5]
|
||||
return True
|
||||
|
||||
for i, attr in enumerate(self.attributes):
|
||||
if i==0:
|
||||
continue
|
||||
|
||||
combination_source = []
|
||||
|
||||
# loop through previous attributes
|
||||
for prev_attr in self.attributes[:i]:
|
||||
combination_source.append([context.selected_attributes[prev_attr.attribute]])
|
||||
|
||||
combination_source.append(context.attribute_values[attr.attribute])
|
||||
|
||||
for combination in itertools.product(*combination_source):
|
||||
if not find_variant(combination):
|
||||
context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1])
|
||||
|
||||
def check_warehouse_is_set_for_stock_item(self):
|
||||
if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
|
||||
frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
|
||||
@@ -410,10 +464,11 @@ class Item(WebsiteGenerator):
|
||||
if not template_item.show_in_website:
|
||||
template_item.show_in_website = 1
|
||||
template_item.flags.ignore_permissions = True
|
||||
template_item.flags.dont_update_variants = True
|
||||
template_item.save()
|
||||
|
||||
def update_variants(self):
|
||||
if self.has_variants:
|
||||
if self.has_variants and not self.flags.dont_update_variants:
|
||||
updated = []
|
||||
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name })
|
||||
for d in variants:
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Warehouse",
|
||||
"label": "From Warehouse",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
@@ -179,7 +179,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Target Warehouse",
|
||||
"label": "To Warehouse (Optional)",
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
@@ -511,7 +511,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-10-12 07:38:58.896987",
|
||||
"modified": "2015-10-26 02:25:47.718911",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Packed Item",
|
||||
|
||||
@@ -325,7 +325,7 @@ def get_pos_profile(company):
|
||||
def get_serial_nos_by_fifo(args, item_doc):
|
||||
if frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo"):
|
||||
return "\n".join(frappe.db.sql_list("""select name from `tabSerial No`
|
||||
where item_code=%(item_code)s and warehouse=%(warehouse)s and status='Available'
|
||||
where item_code=%(item_code)s and warehouse=%(warehouse)s
|
||||
order by timestamp(purchase_date, purchase_time) asc limit %(qty)s""", {
|
||||
"item_code": args.item_code,
|
||||
"warehouse": args.warehouse,
|
||||
|
||||
@@ -7,10 +7,10 @@ from frappe import _
|
||||
from frappe.utils import flt, cint, getdate
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
if not filters: filters = {}
|
||||
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 3
|
||||
|
||||
|
||||
columns = get_columns(filters)
|
||||
item_map = get_item_details(filters)
|
||||
iwb_map = get_item_warehouse_batch_map(filters, float_precision)
|
||||
@@ -32,8 +32,8 @@ def get_columns(filters):
|
||||
"""return columns based on filters"""
|
||||
|
||||
columns = [_("Item") + ":Link/Item:100"] + [_("Item Name") + "::150"] + [_("Description") + "::150"] + \
|
||||
[_("Warehouse") + ":Link/Warehouse:100"] + [_("Batch") + ":Link/Batch:100"] + [_("Opening Qty") + "::90"] + \
|
||||
[_("In Qty") + "::80"] + [_("Out Qty") + "::80"] + [_("Balance Qty") + "::90"]
|
||||
[_("Warehouse") + ":Link/Warehouse:100"] + [_("Batch") + ":Link/Batch:100"] + [_("Opening Qty") + ":Float:90"] + \
|
||||
[_("In Qty") + ":Float:80"] + [_("Out Qty") + ":Float:80"] + [_("Balance Qty") + ":Float:90"]
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-01-14 10:52:58",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"json": "{\"filters\":[[\"Serial No\",\"status\",\"=\",\"Delivered\"]],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"amc_expiry_date\",\"Serial No\"],[\"maintenance_status\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"]],\"sort_by\":\"Serial No.amc_expiry_date\",\"sort_order\":\"asc\",\"sort_by_next\":\"\",\"sort_order_next\":\"desc\"}",
|
||||
"modified": "2014-06-03 07:18:17.322563",
|
||||
"json": "{\"filters\":[[\"Serial No\",\"delivery_document_type\",\"in\",[\"Delivery Note\",\"Sales Invoice\"]],[\"Serial No\",\"warehouse\",\"=\",\"\"]],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"amc_expiry_date\",\"Serial No\"],[\"maintenance_status\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"]],\"sort_by\":\"Serial No.modified\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
|
||||
"modified": "2015-10-22 14:53:45.192497",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No Service Contract Expiry",
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"json": "{\"filters\":[],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"warehouse\",\"Serial No\"],[\"status\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"],[\"purchase_document_no\",\"Serial No\"],[\"purchase_date\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"purchase_rate\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"delivery_date\",\"Serial No\"],[\"supplier\",\"Serial No\"],[\"supplier_name\",\"Serial No\"]],\"sort_by\":\"Serial No.name\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
|
||||
"modified": "2015-09-20 21:09:49.441973",
|
||||
"json": "{\"filters\":[],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"warehouse\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"],[\"purchase_document_no\",\"Serial No\"],[\"purchase_date\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"purchase_rate\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"delivery_date\",\"Serial No\"],[\"supplier\",\"Serial No\"],[\"supplier_name\",\"Serial No\"]],\"sort_by\":\"Serial No.name\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
|
||||
"modified": "2015-10-22 14:49:29.491790",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No Status",
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-01-14 10:52:58",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"json": "{\"filters\":[[\"Serial No\",\"status\",\"=\",\"Delivered\"]],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"warranty_expiry_date\",\"Serial No\"],[\"warranty_period\",\"Serial No\"],[\"maintenance_status\",\"Serial No\"],[\"purchase_document_no\",\"Serial No\"],[\"purchase_date\",\"Serial No\"],[\"supplier\",\"Serial No\"],[\"supplier_name\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"delivery_date\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"]],\"sort_by\":\"Serial No.warranty_expiry_date\",\"sort_order\":\"asc\",\"sort_by_next\":\"\",\"sort_order_next\":\"asc\"}",
|
||||
"modified": "2014-06-03 07:18:17.332902",
|
||||
"json": "{\"filters\":[[\"Serial No\",\"delivery_document_type\",\"in\",[\"Delivery Note\",\"Sales Invoice\"]],[\"Serial No\",\"warehouse\",\"=\",\"\"]],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"warranty_expiry_date\",\"Serial No\"],[\"warranty_period\",\"Serial No\"],[\"maintenance_status\",\"Serial No\"],[\"purchase_document_no\",\"Serial No\"],[\"purchase_date\",\"Serial No\"],[\"supplier\",\"Serial No\"],[\"supplier_name\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"delivery_date\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"]],\"sort_by\":\"Serial No.modified\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
|
||||
"modified": "2015-10-22 14:53:12.575608",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No Warranty Expiry",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, today
|
||||
|
||||
def execute(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
@@ -18,12 +19,19 @@ def get_columns():
|
||||
_("Shortage Qty") + ":Float:100"]
|
||||
|
||||
def get_data(filters):
|
||||
item_map = {}
|
||||
bin_list = get_bin_list(filters)
|
||||
item_map = get_item_map(filters.get("item_code"))
|
||||
warehouse_company = {}
|
||||
data = []
|
||||
|
||||
for bin in get_bin_list(filters):
|
||||
item = item_map.setdefault(bin.item_code, frappe.get_doc("Item", bin.item_code))
|
||||
for bin in bin_list:
|
||||
item = item_map.get(bin.item_code)
|
||||
|
||||
if not item:
|
||||
# likely an item that has reached its end of life
|
||||
continue
|
||||
|
||||
# item = item_map.setdefault(bin.item_code, get_item(bin.item_code))
|
||||
company = warehouse_company.setdefault(bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company"))
|
||||
|
||||
if filters.brand and filters.brand != item.brand:
|
||||
@@ -45,7 +53,7 @@ def get_data(filters):
|
||||
|
||||
data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
|
||||
item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty,
|
||||
bin.projected_qty, re_order_level, re_order_qty, re_order_level - bin.projected_qty])
|
||||
bin.projected_qty, re_order_level, re_order_qty, re_order_level - flt(bin.projected_qty)])
|
||||
|
||||
return data
|
||||
|
||||
@@ -61,3 +69,35 @@ def get_bin_list(filters):
|
||||
filters=bin_filters, order_by="item_code, warehouse")
|
||||
|
||||
return bin_list
|
||||
|
||||
def get_item_map(item_code):
|
||||
"""Optimization: get only the item doc and re_order_levels table"""
|
||||
|
||||
condition = ""
|
||||
if item_code:
|
||||
condition = 'and item_code = "{0}"'.format(frappe.db.escape(item_code))
|
||||
|
||||
items = frappe.db.sql("""select * from `tabItem` item
|
||||
where is_stock_item = 1
|
||||
{condition}
|
||||
and (end_of_life > %(today)s or end_of_life is null or end_of_life='0000-00-00')
|
||||
and exists (select name from `tabBin` bin where bin.item_code=item.name)"""\
|
||||
.format(condition=condition), {"today": today()}, as_dict=True)
|
||||
|
||||
condition = ""
|
||||
if item_code:
|
||||
condition = 'where parent="{0}"'.format(frappe.db.escape(item_code))
|
||||
|
||||
reorder_levels = frappe._dict()
|
||||
for ir in frappe.db.sql("""select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1):
|
||||
if ir.parent not in reorder_levels:
|
||||
reorder_levels[ir.parent] = []
|
||||
|
||||
reorder_levels[ir.parent].append(ir)
|
||||
|
||||
item_map = frappe._dict()
|
||||
for item in items:
|
||||
item["reorder_levels"] = reorder_levels.get(item.name) or []
|
||||
item_map[item.name] = item
|
||||
|
||||
return item_map
|
||||
|
||||
@@ -164,7 +164,7 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin
|
||||
|
||||
for d in bin:
|
||||
serial_nos = frappe.db.sql("""select count(name) from `tabSerial No`
|
||||
where item_code=%s and warehouse=%s and status = 'Available' and docstatus < 2""", (d[0], d[1]))
|
||||
where item_code=%s and warehouse=%s and docstatus < 2""", (d[0], d[1]))
|
||||
|
||||
if serial_nos and flt(serial_nos[0][0]) != flt(d[2]):
|
||||
print d[0], d[1], d[2], serial_nos[0][0]
|
||||
|
||||
@@ -182,7 +182,7 @@ class MaintenanceSchedule(TransactionBase):
|
||||
def validate_serial_no(self, serial_nos, amc_start_date):
|
||||
for serial_no in serial_nos:
|
||||
sr_details = frappe.db.get_value("Serial No", serial_no,
|
||||
["warranty_expiry_date", "amc_expiry_date", "status", "delivery_date"], as_dict=1)
|
||||
["warranty_expiry_date", "amc_expiry_date", "warehouse", "delivery_date"], as_dict=1)
|
||||
|
||||
if not sr_details:
|
||||
frappe.throw(_("Serial No {0} not found").format(serial_no))
|
||||
@@ -193,9 +193,10 @@ class MaintenanceSchedule(TransactionBase):
|
||||
if sr_details.amc_expiry_date and sr_details.amc_expiry_date >= amc_start_date:
|
||||
throw(_("Serial No {0} is under maintenance contract upto {1}").format(serial_no, sr_details.amc_start_date))
|
||||
|
||||
if sr_details.status=="Delivered" and sr_details.delivery_date and \
|
||||
if not sr_details.warehouse and sr_details.delivery_date and \
|
||||
sr_details.delivery_date >= amc_start_date:
|
||||
throw(_("Maintenance start date can not be before delivery date for Serial No {0}").format(serial_no))
|
||||
throw(_("Maintenance start date can not be before delivery date for Serial No {0}")
|
||||
.format(serial_no))
|
||||
|
||||
def validate_schedule(self):
|
||||
item_lst1 =[]
|
||||
|
||||
@@ -26,29 +26,37 @@
|
||||
<p class="text-muted">
|
||||
{{ _("Item Code") }}: <span itemprop="productID">{{ name }}</span></p>
|
||||
<br>
|
||||
<div class="item-attribute-selectors">
|
||||
{% if has_variants %}
|
||||
{% for d in attributes %}
|
||||
{% if attribute_values[d.attribute] -%}
|
||||
<div class="item-view-attribute {% if (attribute_values[d.attribute] | len)==1 -%} hidden {%- endif %}"
|
||||
style="margin-bottom: 10px;">
|
||||
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
|
||||
<select class="form-control"
|
||||
style="max-width: 140px"
|
||||
data-attribute="{{ d.attribute }}">
|
||||
{% for value in attribute_values[d.attribute] %}
|
||||
<option value="{{ value }}"
|
||||
{% if selected_attributes and selected_attributes[d.attribute]==value -%}
|
||||
selected
|
||||
{%- elif disabled_attributes and value in disabled_attributes.get(d.attribute, []) -%}
|
||||
disabled
|
||||
{%- endif %}>
|
||||
{{ _(value) }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<br>
|
||||
<div style="min-height: 100px; margin: 10px 0;">
|
||||
<h4 class="item-price" itemprop="price"></h4>
|
||||
<div class="item-stock" itemprop="availablity"></div>
|
||||
<div class="item-cart hide">
|
||||
{% if has_variants %}
|
||||
{% for d in attributes %}
|
||||
<div class="item-view-attribute"
|
||||
style="margin-bottom: 10px;">
|
||||
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
|
||||
<select class="form-control"
|
||||
style="max-width: 140px"
|
||||
data-attribute="{{ d.attribute }}">
|
||||
{% for value in attribute_values[d.attribute] %}
|
||||
<option value="{{ value }}"
|
||||
{% if selected_attributes and selected_attributes[d.attribute]==value -%} selected {%- endif %}>
|
||||
{{ _(value) }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
<div id="item-add-to-cart">
|
||||
<button class="btn btn-primary btn-sm">
|
||||
{{ _("Add to Cart") }}</button>
|
||||
|
||||
@@ -64,8 +64,23 @@ frappe.ready(function() {
|
||||
});
|
||||
});
|
||||
|
||||
$("[itemscope] .item-view-attribute select").on("change", function() {
|
||||
var item_code = encodeURIComponent(get_item_code());
|
||||
$("[itemscope] .item-view-attribute .form-control").on("change", function() {
|
||||
try {
|
||||
var item_code = encodeURIComponent(get_item_code());
|
||||
} catch(e) {
|
||||
// unable to find variant
|
||||
// then chose the closest available one
|
||||
|
||||
var attribute = $(this).attr("data-attribute");
|
||||
var attribute_value = $(this).val()
|
||||
var item_code = update_attribute_selectors(attribute, attribute_value);
|
||||
|
||||
if (!item_code) {
|
||||
msgprint(__("Please select some other value for {0}", [attribute]))
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.search.indexOf(item_code)!==-1) {
|
||||
return;
|
||||
}
|
||||
@@ -83,10 +98,8 @@ var toggle_update_cart = function(qty) {
|
||||
|
||||
function get_item_code() {
|
||||
if(window.variant_info) {
|
||||
attributes = {};
|
||||
$('[itemscope]').find(".item-view-attribute select").each(function() {
|
||||
attributes[$(this).attr('data-attribute')] = $(this).val();
|
||||
});
|
||||
var attributes = get_selected_attributes();
|
||||
|
||||
for(var i in variant_info) {
|
||||
var variant = variant_info[i];
|
||||
var match = true;
|
||||
@@ -106,3 +119,51 @@ function get_item_code() {
|
||||
return item_code;
|
||||
}
|
||||
}
|
||||
|
||||
function update_attribute_selectors(selected_attribute, selected_attribute_value) {
|
||||
// find the closest match keeping the selected attribute in focus and get the item code
|
||||
|
||||
var attributes = get_selected_attributes();
|
||||
|
||||
var previous_match_score = 0;
|
||||
var matched;
|
||||
for(var i in variant_info) {
|
||||
var variant = variant_info[i];
|
||||
var match_score = 0;
|
||||
var has_selected_attribute = false;
|
||||
|
||||
for(var j in variant.attributes) {
|
||||
if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) {
|
||||
match_score = match_score + 1;
|
||||
|
||||
if (variant.attributes[j].attribute==selected_attribute && variant.attributes[j].attribute_value==selected_attribute_value) {
|
||||
has_selected_attribute = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_selected_attribute && (match_score > previous_match_score)) {
|
||||
previous_match_score = match_score;
|
||||
matched = variant;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
for (var j in matched.attributes) {
|
||||
var attr = matched.attributes[j];
|
||||
$('[itemscope]')
|
||||
.find(repl('.item-view-attribute .form-control[data-attribute="%(attribute)s"]', attr))
|
||||
.val(attr.attribute_value);
|
||||
}
|
||||
|
||||
return matched.name;
|
||||
}
|
||||
}
|
||||
|
||||
function get_selected_attributes() {
|
||||
var attributes = {};
|
||||
$('[itemscope]').find(".item-view-attribute .form-control").each(function() {
|
||||
attributes[$(this).attr('data-attribute')] = $(this).val();
|
||||
});
|
||||
return attributes;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3885
erpnext/translations/uk.csv
Normal file
3885
erpnext/translations/uk.csv
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ frappe.ui.form.on("Rename Tool", {
|
||||
select_doctype: frm.doc.select_doctype
|
||||
},
|
||||
callback: function(r) {
|
||||
frm.get_field("rename_log").$wrapper.html(r.message);
|
||||
frm.get_field("rename_log").$wrapper.html(r.message.join("<br>"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.rename_doc import bulk_rename
|
||||
|
||||
class RenameTool(Document):
|
||||
pass
|
||||
@@ -20,37 +20,13 @@ def get_doctypes():
|
||||
@frappe.whitelist()
|
||||
def upload(select_doctype=None, rows=None):
|
||||
from frappe.utils.csvutils import read_csv_content_from_attached_file
|
||||
from frappe.model.rename_doc import rename_doc
|
||||
|
||||
if not select_doctype:
|
||||
select_doctype = frappe.form_dict.select_doctype
|
||||
|
||||
if not frappe.has_permission(select_doctype, "write"):
|
||||
raise frappe.PermissionError
|
||||
|
||||
if not rows:
|
||||
rows = read_csv_content_from_attached_file(frappe.get_doc("Rename Tool", "Rename Tool"))
|
||||
if not rows:
|
||||
frappe.throw(_("Please select a valid csv file with data"))
|
||||
rows = read_csv_content_from_attached_file(frappe.get_doc("Rename Tool", "Rename Tool"))
|
||||
|
||||
max_rows = 500
|
||||
if len(rows) > max_rows:
|
||||
frappe.throw(_("Maximum {0} rows allowed").format(max_rows))
|
||||
return bulk_rename(select_doctype, rows=rows)
|
||||
|
||||
rename_log = []
|
||||
for row in rows:
|
||||
# if row has some content
|
||||
if len(row) > 1 and row[0] and row[1]:
|
||||
try:
|
||||
if rename_doc(select_doctype, row[0], row[1]):
|
||||
rename_log.append(_("Successful: ") + row[0] + " -> " + row[1])
|
||||
frappe.db.commit()
|
||||
else:
|
||||
rename_log.append(_("Ignored: ") + row[0] + " -> " + row[1])
|
||||
except Exception, e:
|
||||
rename_log.append("<span style='color: RED'>" + \
|
||||
_("Failed: ") + row[0] + " -> " + row[1] + "</span>")
|
||||
rename_log.append("<span style='margin-left: 20px;'>" + repr(e) + "</span>")
|
||||
frappe.db.rollback()
|
||||
|
||||
return rename_log
|
||||
|
||||
Reference in New Issue
Block a user