Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60e7f01fd4 | ||
|
|
169089bdde | ||
|
|
c14f80838b | ||
|
|
a5007db902 | ||
|
|
0d58501229 | ||
|
|
7c654cd1bb | ||
|
|
54fc260a42 | ||
|
|
7a39d51366 | ||
|
|
79c94426f7 | ||
|
|
c3ced9a0b5 | ||
|
|
c825575940 | ||
|
|
afd3209017 | ||
|
|
c541b87bb9 | ||
|
|
b42f3e34ef | ||
|
|
6a7edd32aa | ||
|
|
fc307970aa | ||
|
|
b9e7cb02f4 | ||
|
|
a20a419cb8 | ||
|
|
d805bd7daf | ||
|
|
ac53f2dbb1 | ||
|
|
20a7d820ab | ||
|
|
d48c2394e8 | ||
|
|
58101e9e6c | ||
|
|
666131d2fe | ||
|
|
118f043335 | ||
|
|
b4a51ec80b | ||
|
|
6197860643 | ||
|
|
95fbfa4928 | ||
|
|
b7f0a4961e | ||
|
|
9c044eefff | ||
|
|
5951692db0 | ||
|
|
bd4814fbb7 | ||
|
|
7d23e4286e | ||
|
|
15ea751f3c | ||
|
|
c40148e0da | ||
|
|
a9dda232b2 | ||
|
|
1f25c45ad7 | ||
|
|
12ffd914ee | ||
|
|
b1d8979a59 | ||
|
|
ec2d0030b7 | ||
|
|
55d0506155 | ||
|
|
5e9b52c273 | ||
|
|
00b4663e12 | ||
|
|
e54a4004ec | ||
|
|
d0b086ca54 | ||
|
|
8d1191ac8f | ||
|
|
a938b81e1c | ||
|
|
7cd0ba70d9 | ||
|
|
66340f9894 | ||
|
|
41f7f7442b | ||
|
|
126fb31f9a | ||
|
|
a8a91cca16 | ||
|
|
381385d19a | ||
|
|
aba8fdd18d | ||
|
|
bacc679df5 | ||
|
|
b0388d971a | ||
|
|
61c9ea938d | ||
|
|
6956eee790 | ||
|
|
2e65aadb1e | ||
|
|
653cffec1e | ||
|
|
cc8f1afa56 | ||
|
|
2f702dcb32 | ||
|
|
f857d81f35 | ||
|
|
6d64fe378d | ||
|
|
b0ab93f779 | ||
|
|
df1c1a573f | ||
|
|
a1f2aec918 | ||
|
|
d8930a776d | ||
|
|
8a8ef85174 | ||
|
|
edba048c14 | ||
|
|
b705798ccb | ||
|
|
5e0b0b4b97 | ||
|
|
a4efbf0db7 | ||
|
|
e930f0f74e | ||
|
|
99543f72d8 | ||
|
|
1a9646739a | ||
|
|
bd65cb8817 | ||
|
|
98b287565a | ||
|
|
c6dbe70256 | ||
|
|
556536615e | ||
|
|
8bd96f1c08 | ||
|
|
5b7e9a1c94 | ||
|
|
c306b21415 | ||
|
|
c39cef363c | ||
|
|
baefec4498 | ||
|
|
02a56b4e1a | ||
|
|
9a6df0341f | ||
|
|
5a90e3b2e9 | ||
|
|
7dab3c1f85 | ||
|
|
f9a974385a | ||
|
|
20a653e829 | ||
|
|
a2c668cb77 | ||
|
|
d0b0a80be3 | ||
|
|
532b9e8bfb | ||
|
|
32e48bb568 | ||
|
|
21e09a2bd8 | ||
|
|
1aa6e98136 | ||
|
|
023c036afa | ||
|
|
8372c44262 | ||
|
|
6d69ca6bac | ||
|
|
283b55f88c | ||
|
|
4757d0634a | ||
|
|
dc8ce7f7e9 | ||
|
|
d02375e89d | ||
|
|
a90a0528aa | ||
|
|
350f9592d3 | ||
|
|
43e50de6ef | ||
|
|
a530f410e3 | ||
|
|
bdfb070ed6 | ||
|
|
caa9fc033f | ||
|
|
15bf4e5599 | ||
|
|
6d490e530a | ||
|
|
9b363fe5f1 | ||
|
|
3c5df9f64c | ||
|
|
fd53991dfa | ||
|
|
c794ca53fb | ||
|
|
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 |
@@ -6,7 +6,7 @@
|
||||
|
||||
Includes: Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Requires MariaDB.
|
||||
|
||||
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & Javascript.
|
||||
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
|
||||
|
||||
- [User Guide](https://manual.erpnext.com)
|
||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
from __future__ import unicode_literals
|
||||
__version__ = '6.5.2'
|
||||
__version__ = '6.7.3'
|
||||
|
||||
@@ -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,134 @@ 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));
|
||||
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ from frappe import msgprint, _, scrub
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
|
||||
from erpnext.accounts.party import get_party_account
|
||||
|
||||
class JournalEntry(AccountsController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
@@ -27,6 +27,7 @@ class JournalEntry(AccountsController):
|
||||
self.validate_cheque_info()
|
||||
self.validate_entries_for_advance()
|
||||
self.validate_multi_currency()
|
||||
self.set_amounts_in_company_currency()
|
||||
self.validate_debit_and_credit()
|
||||
self.validate_against_jv()
|
||||
self.validate_reference_doc()
|
||||
@@ -175,6 +176,9 @@ class JournalEntry(AccountsController):
|
||||
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
||||
[scrub(dt) for dt in field_dict.get(d.reference_type)])
|
||||
|
||||
if not against_voucher:
|
||||
frappe.throw(_("Row {0}: Invalid reference {1}").format(d.idx, d.reference_name))
|
||||
|
||||
# check if party and account match
|
||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
|
||||
@@ -279,7 +283,8 @@ class JournalEntry(AccountsController):
|
||||
frappe.throw(_("Please check Multi Currency option to allow accounts with other currency"))
|
||||
|
||||
self.set_exchange_rate()
|
||||
|
||||
|
||||
def set_amounts_in_company_currency(self):
|
||||
for d in self.get("accounts"):
|
||||
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
|
||||
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
|
||||
@@ -498,222 +503,135 @@ 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),
|
||||
"account_currency": account_details.account_currency,
|
||||
"account_type": account_details.account_type
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_sales_invoice(sales_invoice):
|
||||
"""Returns new Journal Entry document as dict for given Sales Invoice"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
si = frappe.get_doc("Sales Invoice", sales_invoice)
|
||||
|
||||
# exchange rate
|
||||
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company,
|
||||
si.doctype, si.name)
|
||||
|
||||
jv = get_payment_entry(si)
|
||||
jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
|
||||
|
||||
# credit customer
|
||||
row1 = jv.get("accounts")[0]
|
||||
row1.account = si.debit_to
|
||||
row1.account_currency = si.party_account_currency
|
||||
row1.party_type = "Customer"
|
||||
row1.party = si.customer
|
||||
row1.balance = get_balance_on(si.debit_to)
|
||||
row1.party_balance = get_balance_on(party=si.customer, party_type="Customer")
|
||||
row1.credit_in_account_currency = si.outstanding_amount
|
||||
row1.reference_type = si.doctype
|
||||
row1.reference_name = si.name
|
||||
row1.exchange_rate = exchange_rate
|
||||
row1.account_type = "Receivable" if si.customer else ""
|
||||
|
||||
# debit bank
|
||||
row2 = jv.get("accounts")[1]
|
||||
if row2.account_currency == si.party_account_currency:
|
||||
row2.debit_in_account_currency = si.outstanding_amount
|
||||
def get_payment_entry_against_order(dt, dn):
|
||||
ref_doc = frappe.get_doc(dt, dn)
|
||||
|
||||
if flt(ref_doc.per_billed, 2) > 0:
|
||||
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||
|
||||
if dt == "Sales Order":
|
||||
party_type = "Customer"
|
||||
amount_field_party = "credit_in_account_currency"
|
||||
amount_field_bank = "debit_in_account_currency"
|
||||
else:
|
||||
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate
|
||||
|
||||
# set multi currency check
|
||||
if row1.account_currency != si.company_currency or row2.account_currency != si.company_currency:
|
||||
jv.multi_currency = 1
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_purchase_invoice(purchase_invoice):
|
||||
"""Returns new Journal Entry document as dict for given Purchase Invoice"""
|
||||
pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
|
||||
|
||||
exchange_rate = get_exchange_rate(pi.credit_to, pi.party_account_currency, pi.company,
|
||||
pi.doctype, pi.name)
|
||||
|
||||
jv = get_payment_entry(pi)
|
||||
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
|
||||
jv.exchange_rate = exchange_rate
|
||||
|
||||
# credit supplier
|
||||
row1 = jv.get("accounts")[0]
|
||||
row1.account = pi.credit_to
|
||||
row1.account_currency = pi.party_account_currency
|
||||
row1.party_type = "Supplier"
|
||||
row1.party = pi.supplier
|
||||
row1.balance = get_balance_on(pi.credit_to)
|
||||
row1.party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
|
||||
row1.debit_in_account_currency = pi.outstanding_amount
|
||||
row1.reference_type = pi.doctype
|
||||
row1.reference_name = pi.name
|
||||
row1.exchange_rate = exchange_rate
|
||||
row1.account_type = "Payable" if pi.supplier else ""
|
||||
|
||||
# credit bank
|
||||
row2 = jv.get("accounts")[1]
|
||||
if row2.account_currency == pi.party_account_currency:
|
||||
row2.credit_in_account_currency = pi.outstanding_amount
|
||||
else:
|
||||
row2.credit_in_account_currency = pi.outstanding_amount * exchange_rate
|
||||
|
||||
# set multi currency check
|
||||
if row1.account_currency != pi.company_currency or row2.account_currency != pi.company_currency:
|
||||
jv.multi_currency = 1
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_sales_order(sales_order):
|
||||
"""Returns new Journal Entry document as dict for given Sales Order"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
|
||||
so = frappe.get_doc("Sales Order", sales_order)
|
||||
|
||||
if flt(so.per_billed, 2) != 0.0:
|
||||
frappe.throw(_("Can only make payment against unbilled Sales Order"))
|
||||
|
||||
jv = get_payment_entry(so)
|
||||
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
|
||||
|
||||
party_account = get_party_account("Customer", so.customer, so.company)
|
||||
party_type = "Supplier"
|
||||
amount_field_party = "debit_in_account_currency"
|
||||
amount_field_bank = "credit_in_account_currency"
|
||||
|
||||
party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
|
||||
party_account_currency = get_account_currency(party_account)
|
||||
|
||||
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
|
||||
|
||||
if party_account_currency == so.company_currency:
|
||||
amount = flt(so.base_grand_total) - flt(so.advance_paid)
|
||||
|
||||
if party_account_currency == ref_doc.company_currency:
|
||||
amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid)
|
||||
else:
|
||||
amount = flt(so.grand_total) - flt(so.advance_paid)
|
||||
|
||||
# credit customer
|
||||
row1 = jv.get("accounts")[0]
|
||||
row1.account = party_account
|
||||
row1.account_currency = party_account_currency
|
||||
row1.party_type = "Customer"
|
||||
row1.party = so.customer
|
||||
row1.balance = get_balance_on(party_account)
|
||||
row1.party_balance = get_balance_on(party=so.customer, party_type="Customer")
|
||||
row1.credit_in_account_currency = amount
|
||||
row1.reference_type = so.doctype
|
||||
row1.reference_name = so.name
|
||||
row1.is_advance = "Yes"
|
||||
row1.exchange_rate = exchange_rate
|
||||
row1.account_type = "Receivable"
|
||||
|
||||
# debit bank
|
||||
row2 = jv.get("accounts")[1]
|
||||
if row2.account_currency == party_account_currency:
|
||||
row2.debit_in_account_currency = amount
|
||||
else:
|
||||
row2.debit_in_account_currency = amount * exchange_rate
|
||||
|
||||
# set multi currency check
|
||||
if row1.account_currency != so.company_currency or row2.account_currency != so.company_currency:
|
||||
jv.multi_currency = 1
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
||||
|
||||
return get_payment_entry(ref_doc, {
|
||||
"party_type": party_type,
|
||||
"party_account": party_account,
|
||||
"party_account_currency": party_account_currency,
|
||||
"amount_field_party": amount_field_party,
|
||||
"amount_field_bank": amount_field_bank,
|
||||
"amount": amount,
|
||||
"remarks": 'Advance Payment received against {0} {1}'.format(dt, dn),
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_purchase_order(purchase_order):
|
||||
"""Returns new Journal Entry document as dict for given Sales Order"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
po = frappe.get_doc("Purchase Order", purchase_order)
|
||||
|
||||
if flt(po.per_billed, 2) != 0.0:
|
||||
frappe.throw(_("Can only make payment against unbilled Sales Order"))
|
||||
|
||||
jv = get_payment_entry(po)
|
||||
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
|
||||
|
||||
party_account = get_party_account("Supplier", po.supplier, po.company)
|
||||
party_account_currency = get_account_currency(party_account)
|
||||
|
||||
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
|
||||
|
||||
if party_account_currency == po.company_currency:
|
||||
amount = flt(po.base_grand_total) - flt(po.advance_paid)
|
||||
def get_payment_entry_against_invoice(dt, dn):
|
||||
ref_doc = frappe.get_doc(dt, dn)
|
||||
if dt == "Sales Invoice":
|
||||
party_type = "Customer"
|
||||
party_account = ref_doc.debit_to
|
||||
amount_field_party = "credit_in_account_currency"
|
||||
amount_field_bank = "debit_in_account_currency"
|
||||
else:
|
||||
amount = flt(po.grand_total) - flt(po.advance_paid)
|
||||
party_type = "Supplier"
|
||||
party_account = ref_doc.credit_to
|
||||
amount_field_party = "debit_in_account_currency"
|
||||
amount_field_bank = "credit_in_account_currency"
|
||||
|
||||
return get_payment_entry(ref_doc, {
|
||||
"party_type": party_type,
|
||||
"party_account": party_account,
|
||||
"party_account_currency": ref_doc.party_account_currency,
|
||||
"amount_field_party": amount_field_party,
|
||||
"amount_field_bank": amount_field_bank,
|
||||
"amount": ref_doc.outstanding_amount,
|
||||
"remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks),
|
||||
"is_advance": "No"
|
||||
})
|
||||
|
||||
def get_payment_entry(ref_doc, args):
|
||||
cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center")
|
||||
exchange_rate = get_exchange_rate(args.get("party_account"), args.get("party_account_currency"),
|
||||
ref_doc.company, ref_doc.doctype, ref_doc.name)
|
||||
|
||||
# credit customer
|
||||
row1 = jv.get("accounts")[0]
|
||||
row1.account = party_account
|
||||
row1.party_type = "Supplier"
|
||||
row1.party = po.supplier
|
||||
row1.balance = get_balance_on(party_account)
|
||||
row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
|
||||
row1.debit_in_account_currency = amount
|
||||
row1.reference_type = po.doctype
|
||||
row1.reference_name = po.name
|
||||
row1.is_advance = "Yes"
|
||||
row1.exchange_rate = exchange_rate
|
||||
row1.account_type = "Payable"
|
||||
|
||||
# debit bank
|
||||
row2 = jv.get("accounts")[1]
|
||||
if row2.account_currency == party_account_currency:
|
||||
row2.credit_in_account_currency = amount
|
||||
else:
|
||||
row2.credit_in_account_currency = amount * exchange_rate
|
||||
|
||||
# set multi currency check
|
||||
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency:
|
||||
jv.multi_currency = 1
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
def get_payment_entry(doc):
|
||||
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
|
||||
|
||||
jv = frappe.new_doc('Journal Entry')
|
||||
jv.voucher_type = 'Bank Entry'
|
||||
jv.company = doc.company
|
||||
jv.fiscal_year = doc.fiscal_year
|
||||
|
||||
jv.append("accounts")
|
||||
d2 = jv.append("accounts")
|
||||
jv = frappe.new_doc("Journal Entry")
|
||||
jv.update({
|
||||
"voucher_type": "Bank Entry",
|
||||
"company": ref_doc.company,
|
||||
"remark": args.get("remarks")
|
||||
})
|
||||
|
||||
party_row = jv.append("accounts", {
|
||||
"account": args.get("party_account"),
|
||||
"party_type": args.get("party_type"),
|
||||
"party": ref_doc.get(args.get("party_type").lower()),
|
||||
"cost_center": cost_center,
|
||||
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
|
||||
"account_currency": args.get("party_account_currency") or \
|
||||
get_account_currency(args.get("party_account")),
|
||||
"account_balance": get_balance_on(args.get("party_account")),
|
||||
"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
|
||||
"exchange_rate": exchange_rate,
|
||||
args.get("amount_field_party"): args.get("amount"),
|
||||
"is_advance": args.get("is_advance"),
|
||||
"reference_type": ref_doc.doctype,
|
||||
"reference_name": ref_doc.name
|
||||
})
|
||||
|
||||
bank_row = jv.append("accounts")
|
||||
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry")
|
||||
if bank_account:
|
||||
d2.account = bank_account["account"]
|
||||
d2.balance = bank_account["balance"]
|
||||
d2.account_currency = bank_account["account_currency"]
|
||||
d2.account_type = bank_account["account_type"]
|
||||
d2.exchange_rate = get_exchange_rate(bank_account["account"],
|
||||
bank_account["account_currency"], doc.company)
|
||||
bank_row.update(bank_account)
|
||||
bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
|
||||
bank_account["account_currency"], ref_doc.company)
|
||||
|
||||
bank_row.cost_center = cost_center
|
||||
|
||||
if bank_row.account_currency == args.get("party_account_currency"):
|
||||
bank_row.set(args.get("amount_field_bank"), args.get("amount"))
|
||||
else:
|
||||
bank_row.set(args.get("amount_field_bank"), args.get("amount") * exchange_rate)
|
||||
|
||||
return jv
|
||||
# set multi currency check
|
||||
if party_row.account_currency != ref_doc.company_currency \
|
||||
or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
|
||||
jv.multi_currency = 1
|
||||
|
||||
jv.set_amounts_in_company_currency()
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_opening_accounts(company):
|
||||
@@ -775,7 +693,6 @@ def get_party_account_and_balance(company, party_type, party):
|
||||
if not frappe.has_permission("Account"):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
|
||||
from erpnext.accounts.party import get_party_account
|
||||
account = get_party_account(party_type, party, company)
|
||||
|
||||
account_balance = get_balance_on(account=account)
|
||||
@@ -814,11 +731,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()
|
||||
|
||||
@@ -38,7 +38,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
get_query_filters: {
|
||||
supplier: cur_frm.doc.supplier || undefined,
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
status: ["not in", ["Stopped", "Closed"]],
|
||||
per_billed: ["<", 99.99],
|
||||
company: cur_frm.doc.company
|
||||
}
|
||||
@@ -52,6 +52,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
get_query_filters: {
|
||||
supplier: cur_frm.doc.supplier || undefined,
|
||||
docstatus: 1,
|
||||
status: ["!=", "Closed"],
|
||||
company: cur_frm.doc.company
|
||||
}
|
||||
})
|
||||
@@ -135,9 +136,10 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
||||
|
||||
cur_frm.cscript.make_bank_entry = function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_invoice",
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
|
||||
args: {
|
||||
"purchase_invoice": cur_frm.doc.name,
|
||||
"dt": "Purchase Invoice",
|
||||
"dn": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
|
||||
@@ -48,7 +48,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_conversion_rate()
|
||||
self.validate_credit_to_acc()
|
||||
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
|
||||
self.check_for_stopped_status()
|
||||
self.check_for_stopped_or_closed_status()
|
||||
self.validate_with_previous_doc()
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.set_against_expense_account()
|
||||
@@ -103,14 +103,14 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.party_account_currency = account.account_currency
|
||||
|
||||
def check_for_stopped_status(self):
|
||||
def check_for_stopped_or_closed_status(self):
|
||||
check_list = []
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
|
||||
for d in self.get('items'):
|
||||
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
|
||||
check_list.append(d.purchase_order)
|
||||
stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order)
|
||||
if stopped:
|
||||
throw(_("Purchase Order {0} is 'Stopped'").format(d.purchase_order))
|
||||
pc_obj.check_for_stopped_or_closed_status('Purchase Order', d.purchase_order)
|
||||
|
||||
def validate_with_previous_doc(self):
|
||||
super(PurchaseInvoice, self).validate_with_previous_doc({
|
||||
@@ -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"
|
||||
@@ -395,6 +394,8 @@ class PurchaseInvoice(BuyingController):
|
||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
|
||||
|
||||
def on_cancel(self):
|
||||
self.check_for_stopped_or_closed_status()
|
||||
|
||||
if not self.is_return:
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -50,6 +50,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
if(doc.update_stock) this.show_stock_ledger();
|
||||
|
||||
if(doc.docstatus==1 && !doc.is_return) {
|
||||
|
||||
var is_delivered_by_supplier = false;
|
||||
|
||||
is_delivered_by_supplier = cur_frm.doc.items.some(function(item){
|
||||
return item.is_delivered_by_supplier ? true : false;
|
||||
})
|
||||
|
||||
cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'),
|
||||
this.make_sales_return);
|
||||
|
||||
@@ -61,7 +68,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
return item.delivery_note ? true : false;
|
||||
});
|
||||
|
||||
if(!from_delivery_note) {
|
||||
if(!from_delivery_note && !is_delivered_by_supplier) {
|
||||
cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
@@ -104,7 +111,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
source_doctype: "Sales Order",
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
status: ["not in", ["Stopped", "Closed"]],
|
||||
per_billed: ["<", 99.99],
|
||||
customer: cur_frm.doc.customer || undefined,
|
||||
company: cur_frm.doc.company
|
||||
@@ -160,6 +167,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;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -320,9 +330,10 @@ cur_frm.cscript['Make Delivery Note'] = function() {
|
||||
|
||||
cur_frm.cscript.make_bank_entry = function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_invoice",
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
|
||||
args: {
|
||||
"sales_invoice": cur_frm.doc.name
|
||||
"dt": "Sales Invoice",
|
||||
"dn": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Series",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -1168,7 +1168,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Apply Additional Discount On",
|
||||
"label": "Apply Additional Discount On",
|
||||
"no_copy": 0,
|
||||
"options": "\nGrand Total\nNet Total",
|
||||
"permlevel": 0,
|
||||
@@ -2211,7 +2211,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Source",
|
||||
"label": "Source",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "source",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -2308,7 +2308,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Opening Entry",
|
||||
"label": "Is Opening Entry",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "is_opening",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -2332,7 +2332,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "C-Form Applicable",
|
||||
"label": "C-Form Applicable",
|
||||
"no_copy": 1,
|
||||
"options": "No\nYes",
|
||||
"permlevel": 0,
|
||||
@@ -2700,7 +2700,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Recurring Type",
|
||||
"label": "Recurring Type",
|
||||
"no_copy": 1,
|
||||
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
@@ -2951,7 +2951,7 @@
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-10-02 07:39:09.123982",
|
||||
"modified": "2015-10-26 12:12:40.616546",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -52,7 +52,7 @@ class SalesInvoice(SellingController):
|
||||
self.validate_proj_cust()
|
||||
self.validate_with_previous_doc()
|
||||
self.validate_uom_is_integer("stock_uom", "qty")
|
||||
self.check_stop_sales_order("sales_order")
|
||||
self.check_stop_or_close_sales_order("sales_order")
|
||||
self.validate_debit_to_acc()
|
||||
self.validate_fixed_asset_account()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
@@ -117,7 +117,7 @@ class SalesInvoice(SellingController):
|
||||
if cint(self.update_stock) == 1:
|
||||
self.update_stock_ledger()
|
||||
|
||||
self.check_stop_sales_order("sales_order")
|
||||
self.check_stop_or_close_sales_order("sales_order")
|
||||
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name)
|
||||
@@ -667,7 +667,8 @@ def make_delivery_note(source_name, target_doc=None):
|
||||
"sales_order": "against_sales_order",
|
||||
"so_detail": "so_detail"
|
||||
},
|
||||
"postprocess": update_item
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.delivered_by_supplier!=1
|
||||
},
|
||||
"Sales Taxes and Charges": {
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
|
||||
@@ -680,6 +680,51 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.delivered_by_supplier==1",
|
||||
"fieldname": "drop_ship",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Drop Ship",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"fieldname": "delivered_by_supplier",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Delivered By Supplier",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -1282,7 +1327,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-10-19 03:04:52.093181",
|
||||
"modified": "2015-11-02 15:14:02.306067",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-07-30 17:28:49",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2015-03-30 05:33:45.353064",
|
||||
"modified": "2015-11-02 12:32:02.048551",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Delivered Items To Be Billed",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n\t`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n\t`tabDelivery Note`.`posting_date` as \"Date:Date\",\n\t`tabDelivery Note`.`project_name` as \"Project\",\n\t`tabDelivery Note Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabDelivery Note Item`.`qty` - ifnull((select sum(qty) from `tabSales Invoice Item` \n\t where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n\t `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Qty:Float:110\",\n\t(`tabDelivery Note Item`.`base_amount` - ifnull((select sum(base_amount) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Amount:Currency:110\",\n\t`tabDelivery Note Item`.`item_name` as \"Item Name::150\",\n\t`tabDelivery Note Item`.`description` as \"Description::200\",\n\t`tabDelivery Note`.`company` as \"Company:Link/Company:\"\nfrom `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note`.docstatus = 1 and\n\t`tabDelivery Note`.`status` != \"Stopped\" and\n `tabDelivery Note`.name = `tabDelivery Note Item`.parent and\n (`tabDelivery Note Item`.qty > ifnull((select sum(qty) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\norder by `tabDelivery Note`.`name` desc",
|
||||
"query": "select\n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n\t`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n\t`tabDelivery Note`.`posting_date` as \"Date:Date\",\n\t`tabDelivery Note`.`project_name` as \"Project\",\n\t`tabDelivery Note Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabDelivery Note Item`.`qty` - ifnull((select sum(qty) from `tabSales Invoice Item` \n\t where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n\t `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Qty:Float:110\",\n\t(`tabDelivery Note Item`.`base_amount` - ifnull((select sum(base_amount) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Amount:Currency:110\",\n\t`tabDelivery Note Item`.`item_name` as \"Item Name::150\",\n\t`tabDelivery Note Item`.`description` as \"Description::200\",\n\t`tabDelivery Note`.`company` as \"Company:Link/Company:\"\nfrom `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note`.docstatus = 1 and\n\t`tabDelivery Note`.`status` not in (\"Stopped\", \"Closed\") and\n `tabDelivery Note`.name = `tabDelivery Note Item`.parent and\n (`tabDelivery Note Item`.qty > ifnull((select sum(qty) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\norder by `tabDelivery Note`.`name` desc",
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "Delivered Items To Be Billed",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -98,11 +98,17 @@ class GrossProfitGenerator(object):
|
||||
|
||||
row.base_amount = flt(row.base_net_amount)
|
||||
|
||||
product_bundles = self.product_bundles.get(row.parenttype, {}).get(row.parent, frappe._dict())
|
||||
|
||||
if row.update_stock:
|
||||
product_bundles = self.product_bundles.get(row.parenttype, {}).get(row.parent, frappe._dict())
|
||||
elif row.dn_detail:
|
||||
product_bundles = self.product_bundles.get("Delivery Note", {})\
|
||||
.get(row.delivery_note, frappe._dict())
|
||||
row.item_row = row.dn_detail
|
||||
|
||||
# get buying amount
|
||||
if row.item_code in product_bundles:
|
||||
row.buying_amount = self.get_buying_amount_from_product_bundle(row, product_bundles[row.item_code])
|
||||
row.buying_amount = self.get_buying_amount_from_product_bundle(row,
|
||||
product_bundles[row.item_code])
|
||||
else:
|
||||
row.buying_amount = self.get_buying_amount(row, row.item_code)
|
||||
|
||||
@@ -142,7 +148,6 @@ class GrossProfitGenerator(object):
|
||||
new_row.qty += row.qty
|
||||
new_row.buying_amount += row.buying_amount
|
||||
new_row.base_amount += row.base_amount
|
||||
# new_row.allocated_amount += (row.allocated_amount or 0) if new_row.allocated_amount else 0
|
||||
|
||||
new_row.gross_profit = new_row.base_amount - new_row.buying_amount
|
||||
new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \
|
||||
@@ -158,9 +163,9 @@ class GrossProfitGenerator(object):
|
||||
|
||||
def get_buying_amount_from_product_bundle(self, row, product_bundle):
|
||||
buying_amount = 0.0
|
||||
for bom_item in product_bundle:
|
||||
if bom_item.get("parent_detail_docname")==row.item_row:
|
||||
buying_amount += self.get_buying_amount(row, bom_item.item_code)
|
||||
for packed_item in product_bundle:
|
||||
if packed_item.get("parent_detail_docname")==row.item_row:
|
||||
buying_amount += self.get_buying_amount(row, packed_item.item_code)
|
||||
|
||||
return buying_amount
|
||||
|
||||
@@ -176,14 +181,14 @@ class GrossProfitGenerator(object):
|
||||
else:
|
||||
my_sle = self.sle.get((item_code, row.warehouse))
|
||||
if (row.update_stock or row.dn_detail) and my_sle:
|
||||
parenttype, parent, item_row = row.parenttype, row.parent, row.item_row
|
||||
parenttype, parent = row.parenttype, row.parent
|
||||
if row.dn_detail:
|
||||
parenttype, parent, item_row = "Delivery Note", row.delivery_note, row.dn_detail
|
||||
parenttype, parent = "Delivery Note", row.delivery_note
|
||||
|
||||
for i, sle in enumerate(my_sle):
|
||||
# find the stock valution rate from stock ledger entry
|
||||
if sle.voucher_type == parenttype and parent == sle.voucher_no and \
|
||||
sle.voucher_detail_no == item_row:
|
||||
sle.voucher_detail_no == row.item_row:
|
||||
previous_stock_value = len(my_sle) > i+1 and \
|
||||
flt(my_sle[i+1].stock_value) or 0.0
|
||||
return previous_stock_value - flt(sle.stock_value)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-02-21 14:26:44",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2015-03-30 05:33:29.382709",
|
||||
"modified": "2015-11-04 11:56:32.699103",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Ordered Items To Be Billed",
|
||||
"owner": "Administrator",
|
||||
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabSales Order Item`.base_amount, 0) - (ifnull(`tabSales Order Item`.billed_amt, 0) * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.billed_amt,0) < ifnull(`tabSales Order Item`.amount,0)\norder by `tabSales Order`.transaction_date asc",
|
||||
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabSales Order Item`.base_amount, 0) - (ifnull(`tabSales Order Item`.billed_amt, 0) * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.billed_amt,0) < ifnull(`tabSales Order Item`.amount,0)\norder by `tabSales Order`.transaction_date asc",
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "Ordered Items To Be Billed",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, getdate
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
@@ -13,25 +13,27 @@ def execute(filters=None):
|
||||
|
||||
columns = get_columns(filters)
|
||||
entries = get_entries(filters)
|
||||
invoice_posting_date_map = get_invoice_posting_date_map(filters)
|
||||
invoice_details = get_invoice_posting_date_map(filters)
|
||||
against_date = ""
|
||||
|
||||
data = []
|
||||
for d in entries:
|
||||
against_date = invoice_posting_date_map.get(d.reference_name) or ""
|
||||
invoice = invoice_details.get(d.reference_name) or frappe._dict()
|
||||
if d.reference_type=="Purchase Invoice":
|
||||
payment_amount = flt(d.debit) or -1 * flt(d.credit)
|
||||
else:
|
||||
payment_amount = flt(d.credit) or -1 * flt(d.debit)
|
||||
|
||||
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name,
|
||||
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
||||
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
|
||||
invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
||||
|
||||
if d.reference_name:
|
||||
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
|
||||
else:
|
||||
row += ["", "", "", "", ""]
|
||||
|
||||
if invoice.due_date:
|
||||
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
|
||||
|
||||
data.append(row)
|
||||
|
||||
return columns, data
|
||||
@@ -43,13 +45,25 @@ def validate_filters(filters):
|
||||
.format(filters.payment_type, filters.party_type))
|
||||
|
||||
def get_columns(filters):
|
||||
return [_("Journal Entry") + ":Link/Journal Entry:140",
|
||||
_("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140",
|
||||
return [
|
||||
_("Journal Entry") + ":Link/Journal Entry:140",
|
||||
_("Party Type") + "::100",
|
||||
_("Party") + ":Dynamic Link/Party Type:140",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Against Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
||||
_("Against Invoice Posting Date") + ":Date:130", _("Debit") + ":Currency:120", _("Credit") + ":Currency:120",
|
||||
_("Reference No") + "::100", _("Reference Date") + ":Date:100", _("Remarks") + "::150", _("Age") +":Int:40",
|
||||
"0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100"
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
||||
_("Invoice Posting Date") + ":Date:130",
|
||||
_("Payment Due Date") + ":Date:130",
|
||||
_("Debit") + ":Currency:120",
|
||||
_("Credit") + ":Currency:120",
|
||||
_("Reference No") + "::100",
|
||||
_("Reference Date") + ":Date:100",
|
||||
_("Remarks") + "::150",
|
||||
_("Age") +":Int:40",
|
||||
"0-30:Currency:100",
|
||||
"30-60:Currency:100",
|
||||
"60-90:Currency:100",
|
||||
_("90-Above") + ":Currency:100",
|
||||
_("Delay in payment (Days)") + "::150"
|
||||
]
|
||||
|
||||
def get_conditions(filters):
|
||||
@@ -66,7 +80,14 @@ def get_conditions(filters):
|
||||
|
||||
if filters.get("party"):
|
||||
conditions.append("jvd.party=%(party)s")
|
||||
|
||||
|
||||
if filters.get("party_type"):
|
||||
conditions.append("jvd.reference_type=%(reference_type)s")
|
||||
if filters.get("party_type") == "Customer":
|
||||
filters["reference_type"] = "Sales Invoice"
|
||||
else:
|
||||
filters["reference_type"] = "Purchase Invoice"
|
||||
|
||||
if filters.get("company"):
|
||||
conditions.append("jv.company=%(company)s")
|
||||
|
||||
@@ -89,12 +110,9 @@ def get_entries(filters):
|
||||
return entries
|
||||
|
||||
def get_invoice_posting_date_map(filters):
|
||||
invoice_posting_date_map = {}
|
||||
if filters.get("payment_type") == "Incoming":
|
||||
for t in frappe.db.sql("""select name, posting_date from `tabSales Invoice`"""):
|
||||
invoice_posting_date_map[t[0]] = t[1]
|
||||
else:
|
||||
for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""):
|
||||
invoice_posting_date_map[t[0]] = t[1]
|
||||
invoice_details = {}
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
|
||||
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
|
||||
invoice_details[t.name] = t
|
||||
|
||||
return invoice_posting_date_map
|
||||
return invoice_details
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-05-28 15:54:16",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2015-03-30 05:37:23.626083",
|
||||
"modified": "2015-11-04 11:56:14.321664",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Order Items To Be Billed",
|
||||
"owner": "Administrator",
|
||||
"query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc",
|
||||
"query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status not in (\"Stopped\", \"Closed\")\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc",
|
||||
"ref_doctype": "Purchase Invoice",
|
||||
"report_name": "Purchase Order Items To Be Billed",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-07-30 18:35:10",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2015-04-14 11:56:02.323769",
|
||||
"modified": "2015-11-02 12:33:11.681513",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Received Items To Be Billed",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Receipt`.`posting_date` as \"Date:Date\",\n\t`tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabPurchase Receipt Item`.`qty` - ifnull((select sum(qty) from `tabPurchase Invoice Item` \n\t where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n\t `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Qty:Float:110\",\n\t(`tabPurchase Receipt Item`.`base_amount` - ifnull((select sum(base_amount) \n from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Amount:Currency:110\",\n\t`tabPurchase Receipt Item`.`item_name` as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.`description` as \"Description::200\",\n\t`tabPurchase Receipt`.`company` as \"Company:Link/Company:\"\nfrom `tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n `tabPurchase Receipt`.docstatus = 1 and\n `tabPurchase Receipt`.name = `tabPurchase Receipt Item`.parent and\n (`tabPurchase Receipt Item`.qty > ifnull((select sum(qty) from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus=1 and \n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\norder by `tabPurchase Receipt`.`name` desc",
|
||||
"query": "select\n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Receipt`.`posting_date` as \"Date:Date\",\n\t`tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabPurchase Receipt Item`.`qty` - ifnull((select sum(qty) from `tabPurchase Invoice Item` \n\t where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n\t `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Qty:Float:110\",\n\t(`tabPurchase Receipt Item`.`base_amount` - ifnull((select sum(base_amount) \n from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Amount:Currency:110\",\n\t`tabPurchase Receipt Item`.`item_name` as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.`description` as \"Description::200\",\n\t`tabPurchase Receipt`.`company` as \"Company:Link/Company:\"\nfrom `tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n `tabPurchase Receipt`.docstatus = 1 and `tabPurchase Receipt`.status != \"Closed\" and \n `tabPurchase Receipt`.name = `tabPurchase Receipt Item`.parent and\n (`tabPurchase Receipt Item`.qty > ifnull((select sum(qty) from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus=1 and \n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\norder by `tabPurchase Receipt`.`name` desc",
|
||||
"ref_doctype": "Purchase Invoice",
|
||||
"report_name": "Received Items To Be Billed",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -42,6 +42,8 @@ class PurchaseCommon(BuyingController):
|
||||
items = []
|
||||
for d in obj.get("items"):
|
||||
if not d.qty:
|
||||
if obj.doctype == "Purchase Receipt" and d.rejected_qty:
|
||||
continue
|
||||
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
||||
|
||||
# udpate with latest quantities
|
||||
@@ -56,11 +58,11 @@ class PurchaseCommon(BuyingController):
|
||||
d.set(x, f_lst[x])
|
||||
|
||||
item = frappe.db.sql("""select is_stock_item, is_purchase_item,
|
||||
is_sub_contracted_item, end_of_life from `tabItem` where name=%s""",
|
||||
is_sub_contracted_item, end_of_life, disabled from `tabItem` where name=%s""",
|
||||
d.item_code, as_dict=1)[0]
|
||||
|
||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||
validate_end_of_life(d.item_code, item.end_of_life)
|
||||
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
|
||||
|
||||
# validate stock item
|
||||
if item.is_stock_item==1 and d.qty and not d.warehouse:
|
||||
@@ -72,17 +74,18 @@ class PurchaseCommon(BuyingController):
|
||||
frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx))
|
||||
|
||||
items.append(cstr(d.item_code))
|
||||
|
||||
if items and len(items) != len(set(items)) and \
|
||||
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
|
||||
frappe.msgprint(_("Warning: Same item has been entered multiple times."))
|
||||
|
||||
|
||||
def check_for_stopped_status(self, doctype, docname):
|
||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
||||
if stopped:
|
||||
frappe.throw(_("{0} {1} status is 'Stopped'").format(doctype, docname), frappe.InvalidStatusError)
|
||||
|
||||
def check_for_stopped_or_closed_status(self, doctype, docname):
|
||||
status = frappe.db.get_value(doctype, docname, "status")
|
||||
|
||||
if status in ("Stopped", "Closed"):
|
||||
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
|
||||
|
||||
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
|
||||
if check == 'Next':
|
||||
submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2
|
||||
|
||||
@@ -19,10 +19,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
this._super();
|
||||
// this.frm.dashboard.reset();
|
||||
|
||||
if(doc.docstatus == 1 && doc.status != 'Stopped') {
|
||||
if(doc.docstatus == 1 && !in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
|
||||
|
||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
|
||||
cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order);
|
||||
}
|
||||
|
||||
cur_frm.add_custom_button(__('Close'), this.close_purchase_order);
|
||||
|
||||
if(doc.delivered_by_supplier && doc.status!="Delivered"){
|
||||
cur_frm.add_custom_button(__('Mark as Delivered'), this.delivered_by_supplier);
|
||||
}
|
||||
|
||||
if(flt(doc.per_billed)==0) {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||
@@ -45,8 +52,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
cur_frm.cscript.add_from_mappers();
|
||||
}
|
||||
|
||||
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
||||
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']);
|
||||
if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
|
||||
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
|
||||
}
|
||||
},
|
||||
|
||||
make_stock_entry: function() {
|
||||
@@ -146,15 +154,28 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
|
||||
make_bank_entry: function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_order",
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
|
||||
args: {
|
||||
"purchase_order": cur_frm.doc.name
|
||||
"dt": "Purchase Order",
|
||||
"dn": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
},
|
||||
stop_purchase_order: function(){
|
||||
cur_frm.cscript.update_status('Stop', 'Stopped')
|
||||
},
|
||||
unstop_purchase_order: function(){
|
||||
cur_frm.cscript.update_status('Re-open', 'Submitted')
|
||||
},
|
||||
close_purchase_order: function(){
|
||||
cur_frm.cscript.update_status('Close', 'Closed')
|
||||
},
|
||||
delivered_by_supplier: function(){
|
||||
cur_frm.cscript.update_status('Deliver', 'Delivered')
|
||||
}
|
||||
|
||||
});
|
||||
@@ -162,6 +183,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.update_status= function(label, status){
|
||||
frappe.call({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.update_status",
|
||||
args: {status: status, name: cur_frm.doc.name},
|
||||
callback: function(r) {
|
||||
cur_frm.set_value("status", status);
|
||||
cur_frm.reload_doc();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
|
||||
return {
|
||||
filters: {'supplier': doc.supplier}
|
||||
@@ -201,28 +233,6 @@ cur_frm.cscript.get_last_purchase_rate = function(doc, cdt, cdn){
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Stop Purchase Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm(__("Do you really want to STOP ") + doc.name);
|
||||
|
||||
if (check) {
|
||||
return $c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs':doc}, function(r,rt) {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript['Unstop Purchase Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm(__("Do you really want to UNSTOP ") + doc.name);
|
||||
|
||||
if (check) {
|
||||
return $c('runserverobj', args={'method':'update_status', 'arg': 'Submitted', 'docs':doc}, function(r,rt) {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.pformat.indent_no = function(doc, cdt, cdn){
|
||||
//function to make row of table
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
@@ -326,6 +327,255 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "delivered_by_supplier",
|
||||
"fieldname": "drop_ship",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Drop Ship",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"depends_on": "delivered_by_supplier",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer",
|
||||
"no_copy": 0,
|
||||
"options": "Customer",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "delivered_by_supplier",
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Name",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "delivered_by_supplier",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "To be delivered to customer",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"depends_on": "delivered_by_supplier",
|
||||
"fieldname": "customer_address",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Address",
|
||||
"no_copy": 0,
|
||||
"options": "Address",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"depends_on": "delivered_by_supplier",
|
||||
"fieldname": "customer_contact_person",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Contact",
|
||||
"no_copy": 0,
|
||||
"options": "Contact",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"fieldname": "customer_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Address Display",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "customer_contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Contact",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "customer_contact_mobile",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Mobile No",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "customer_contact_email",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Contact Email",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -1479,7 +1729,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nDraft\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nStopped\nCancelled",
|
||||
"options": "\nDraft\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nStopped\nCancelled\nClosed\nDelivered",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
@@ -2033,7 +2283,7 @@
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-10-16 06:13:50.058318",
|
||||
"modified": "2015-11-04 04:44:22.025827",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -39,7 +39,7 @@ class PurchaseOrder(BuyingController):
|
||||
self.set_status()
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
pc_obj.validate_for_items(self)
|
||||
self.check_for_stopped_status(pc_obj)
|
||||
self.check_for_stopped_or_closed_status(pc_obj)
|
||||
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
|
||||
@@ -108,12 +108,12 @@ class PurchaseOrder(BuyingController):
|
||||
= d.rate = item_last_purchase_rate
|
||||
|
||||
# Check for Stopped status
|
||||
def check_for_stopped_status(self, pc_obj):
|
||||
def check_for_stopped_or_closed_status(self, pc_obj):
|
||||
check_list =[]
|
||||
for d in self.get('items'):
|
||||
if d.meta.get_field('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_docname not in check_list:
|
||||
check_list.append(d.prevdoc_docname)
|
||||
pc_obj.check_for_stopped_status( d.prevdoc_doctype, d.prevdoc_docname)
|
||||
pc_obj.check_for_stopped_or_closed_status( d.prevdoc_doctype, d.prevdoc_docname)
|
||||
|
||||
def update_requested_qty(self):
|
||||
material_request_map = {}
|
||||
@@ -154,14 +154,16 @@ class PurchaseOrder(BuyingController):
|
||||
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
self.db_set('status', status)
|
||||
self.set_status(update=True)
|
||||
self.set_status(update=True, status=status)
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
self.notify_update()
|
||||
clear_doctype_notifications(self)
|
||||
|
||||
def on_submit(self):
|
||||
if self.delivered_by_supplier == 1:
|
||||
self.update_status_updater()
|
||||
|
||||
super(PurchaseOrder, self).on_submit()
|
||||
|
||||
purchase_controller = frappe.get_doc("Purchase Common")
|
||||
@@ -176,8 +178,11 @@ class PurchaseOrder(BuyingController):
|
||||
purchase_controller.update_last_purchase_rate(self, is_submit = 1)
|
||||
|
||||
def on_cancel(self):
|
||||
if self.delivered_by_supplier == 1:
|
||||
self.update_status_updater()
|
||||
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
self.check_for_stopped_status(pc_obj)
|
||||
self.check_for_stopped_or_closed_status(pc_obj)
|
||||
|
||||
# Check if Purchase Receipt has been submitted against current Purchase Order
|
||||
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item')
|
||||
@@ -214,6 +219,28 @@ class PurchaseOrder(BuyingController):
|
||||
"prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"):
|
||||
d.set(field, None)
|
||||
|
||||
def update_status_updater(self):
|
||||
self.status_updater[0].update({
|
||||
"target_parent_dt": "Sales Order",
|
||||
"target_dt": "Sales Order Item",
|
||||
'target_field': 'ordered_qty',
|
||||
"target_parent_field": ''
|
||||
})
|
||||
|
||||
def update_delivered_qty_in_sales_order(self):
|
||||
"""Update delivered qty in Sales Order for drop ship"""
|
||||
sales_orders_to_update = []
|
||||
for item in self.items:
|
||||
if item.prevdoc_doctype == "Sales Order":
|
||||
if item.prevdoc_docname not in sales_orders_to_update:
|
||||
sales_orders_to_update.append(item.prevdoc_docname)
|
||||
|
||||
for so_name in sales_orders_to_update:
|
||||
so = frappe.get_doc("Sales Order", so_name)
|
||||
so.update_delivery_status(self.name)
|
||||
so.set_status(update=True)
|
||||
so.notify_update()
|
||||
|
||||
@frappe.whitelist()
|
||||
def stop_or_unstop_purchase_orders(names, status):
|
||||
if not frappe.has_permission("Purchase Order", "write"):
|
||||
@@ -223,11 +250,11 @@ def stop_or_unstop_purchase_orders(names, status):
|
||||
for name in names:
|
||||
po = frappe.get_doc("Purchase Order", name)
|
||||
if po.docstatus == 1:
|
||||
if status=="Stopped":
|
||||
if po.status not in ("Stopped", "Cancelled") and (po.per_received < 100 or po.per_billed < 100):
|
||||
po.update_status("Stopped")
|
||||
if status in ("Stopped", "Closed"):
|
||||
if po.status not in ("Stopped", "Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
|
||||
po.update_status(status)
|
||||
else:
|
||||
if po.status == "Stopped":
|
||||
if po.status in ("Stopped", "Closed"):
|
||||
po.update_status("Draft")
|
||||
|
||||
frappe.local.message_log = []
|
||||
@@ -325,3 +352,10 @@ def make_stock_entry(purchase_order, item_code):
|
||||
stock_entry.bom_no = po_item.bom
|
||||
stock_entry.get_items()
|
||||
return stock_entry.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_status(status, name):
|
||||
po = frappe.get_doc("Purchase Order", name)
|
||||
po.update_status(status)
|
||||
po.update_delivered_qty_in_sales_order()
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ frappe.listview_settings['Purchase Order'] = {
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status==="Stopped") {
|
||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
||||
} else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
|
||||
} else if(doc.status==="Closed"){
|
||||
return [__("Closed"), "green", "status,=,Closed"];
|
||||
} else if (doc.status==="Delivered") {
|
||||
return [__("Delivered"), "green", "status,=,Closed"];
|
||||
}else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
return [__("To Receive and Bill"), "orange",
|
||||
"per_received,<,100|per_billed,<,100|status,!=,Stopped"];
|
||||
@@ -21,13 +25,16 @@ frappe.listview_settings['Purchase Order'] = {
|
||||
onload: function(listview) {
|
||||
var method = "erpnext.buying.doctype.purchase_order.purchase_order.stop_or_unstop_purchase_orders";
|
||||
|
||||
listview.page.add_menu_item(__("Set as Stopped"), function() {
|
||||
listview.page.add_menu_item(__("Close"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Closed"});
|
||||
});
|
||||
|
||||
listview.page.add_menu_item(__("Stop"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Stopped"});
|
||||
});
|
||||
|
||||
listview.page.add_menu_item(__("Set as Unstopped"), function() {
|
||||
listview.page.add_menu_item(__("Re-open"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Submitted"});
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,6 +70,20 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
from erpnext.utilities.transaction_base import UOMMustBeIntegerError
|
||||
po = create_purchase_order(qty=3.4, do_not_save=True)
|
||||
self.assertRaises(UOMMustBeIntegerError, po.insert)
|
||||
|
||||
def test_ordered_qty_for_closing_po(self):
|
||||
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
||||
fields=["ordered_qty"])
|
||||
|
||||
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
|
||||
|
||||
po = create_purchase_order(item_code= "_Test Item", qty=1)
|
||||
|
||||
self.assertEquals(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1)
|
||||
|
||||
po.update_status("Closed")
|
||||
|
||||
self.assertEquals(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty)
|
||||
|
||||
def create_purchase_order(**args):
|
||||
po = frappe.new_doc("Purchase Order")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
0
erpnext/buying/print_format/__init__.py
Normal file
0
erpnext/buying/print_format/__init__.py
Normal file
17
erpnext/buying/print_format/drop_shipping/drop_shipping.json
Normal file
17
erpnext/buying/print_format/drop_shipping/drop_shipping.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"creation": "2015-10-20 16:46:39.382799",
|
||||
"custom_format": 0,
|
||||
"disabled": 0,
|
||||
"doc_type": "Purchase Order",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font": "Default",
|
||||
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \"<h1 style=\\\"text-align: center;\\\">Purchase Order</h1><div style=\\\"text-align: center;\\\">{{doc.name}}</div><div style=\\\"text-align: center;\\\"><hr></div>\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"title\"}, {\"print_hide\": 0, \"fieldname\": \"supplier\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_name\"}, {\"print_hide\": 0, \"fieldname\": \"address_display\"}, {\"print_hide\": 0, \"fieldname\": \"contact_display\"}, {\"print_hide\": 0, \"fieldname\": \"contact_mobile\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"transaction_date\"}, {\"print_hide\": 0, \"fieldname\": \"customer\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_address_display\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_display\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_mobile\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"image\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"60px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"100px\"}, {\"print_hide\": 0, \"fieldname\": \"discount_percentage\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"pricing_rule\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_quotation\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_quotation_item\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\"}, {\"print_hide\": 0, \"fieldname\": \"get_last_purchase_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"category\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"add_deduct_tax\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"included_in_print_rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"terms\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_person\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"recurring_print_format\"}]",
|
||||
"modified": "2015-10-20 17:21:45.810640",
|
||||
"modified_by": "saurabh@erpnext.com",
|
||||
"name": "Drop Shipping",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 1,
|
||||
"print_format_type": "Server",
|
||||
"standard": "No"
|
||||
}
|
||||
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
|
||||
7
erpnext/change_log/v6/v6_7_0.md
Normal file
7
erpnext/change_log/v6/v6_7_0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
- **Desktop Reorganization:** To Do, Calendar, Messages, Notes, Activty have been moved into module **Tools**
|
||||
- Integrations and Installer has been moved into **Setup**
|
||||
- Make Purchase Order from Sales Order if Supplier is mentioned.
|
||||
- **Drop Ship Integration:** Make Sales Order with item marked as **Supplier delivers to Customer** and then make a Purchase Order from the Sales Order with Supplier details.
|
||||
- Customer details in Purchase Order for Drop Ship.
|
||||
- Set Sales Order, Purchase Order, Delivery Note and Purchase Receipt as **Closed** to clear notifications.
|
||||
- Allocate leaves in **Leave Allocation** by specific dates and not Fiscal Year. Sponsored by [Believer's Church](https://www.believerschurch.com)
|
||||
@@ -164,6 +164,21 @@ def get_data():
|
||||
"label": _("Customer and Supplier"),
|
||||
"youtube_id": "anoGi_RpQ20"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Material Request to Purchase Order"),
|
||||
"youtube_id": "4TN9kPyfIqM"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Purchase Order to Payment"),
|
||||
"youtube_id": "EK65tLdVUDk"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Managing Subcontracting"),
|
||||
"youtube_id": "ThiMCC2DtKo"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -146,6 +146,11 @@ def get_data():
|
||||
"label": _("Lead to Quotation"),
|
||||
"youtube_id": "TxYX4r4JAKA"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Newsletters"),
|
||||
"youtube_id": "muLKsCrrDRo"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -64,9 +64,9 @@ def get_data():
|
||||
"type": "module"
|
||||
},
|
||||
"Learn": {
|
||||
"color": "#FCB868",
|
||||
"color": "#FF888B",
|
||||
"force_show": True,
|
||||
"icon": "icon-facetime-video",
|
||||
"icon": "octicon octicon-device-camera-video",
|
||||
"type": "module",
|
||||
"is_help": True
|
||||
}
|
||||
|
||||
@@ -196,4 +196,30 @@ def get_data():
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Help"),
|
||||
"icon": "icon-facetime-video",
|
||||
"items": [
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Setting up Employees"),
|
||||
"youtube_id": "USfIUdZlUhw"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Leave Management"),
|
||||
"youtube_id": "fc0p_AXebc8"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Expense Claims"),
|
||||
"youtube_id": "5SZHJF--ZFY"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Processing Payroll"),
|
||||
"youtube_id": "apgE-f25Rm0"
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -122,6 +122,11 @@ def get_data():
|
||||
"label": _("Items and Pricing"),
|
||||
"youtube_id": "qXaEwld4_Ps"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Item Variants"),
|
||||
"youtube_id": "OGBETlCzU5o"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Opening Stock Balance"),
|
||||
|
||||
@@ -120,6 +120,16 @@ def get_data():
|
||||
"label": _("Bill of Materials"),
|
||||
"youtube_id": "hDV0c1OeWLo"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Production Planning Tool"),
|
||||
"youtube_id": "CzatSl4zJ2Y"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Production Order"),
|
||||
"youtube_id": "ZotgLyp2YFY"
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -70,4 +70,15 @@ def get_data():
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Help"),
|
||||
"icon": "icon-facetime-video",
|
||||
"items": [
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Managing Projects"),
|
||||
"youtube_id": "egxIGwtoKI4"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -294,6 +294,16 @@ def get_data():
|
||||
"label": _("Customer and Supplier"),
|
||||
"youtube_id": "anoGi_RpQ20"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Sales Order to Payment"),
|
||||
"youtube_id": "7AMq4lqkN4A"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Point-of-Sale"),
|
||||
"youtube_id": "4WkelWkbP_c"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -7,6 +7,11 @@ def get_data():
|
||||
"label": _("Documents"),
|
||||
"icon": "icon-star",
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item",
|
||||
"description": _("All Products or Services."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Material Request",
|
||||
@@ -32,11 +37,6 @@ def get_data():
|
||||
"name": "Installation Note",
|
||||
"description": _("Installation record for a Serial No.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item",
|
||||
"description": _("All Products or Services."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Warehouse",
|
||||
@@ -263,6 +263,11 @@ def get_data():
|
||||
"label": _("Items and Pricing"),
|
||||
"youtube_id": "qXaEwld4_Ps"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Item Variants"),
|
||||
"youtube_id": "OGBETlCzU5o"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Opening Stock Balance"),
|
||||
@@ -270,8 +275,23 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Item Variants"),
|
||||
"youtube_id": "OGBETlCzU5o"
|
||||
"label": _("Making Stock Entries"),
|
||||
"youtube_id": "Njt107hlY3I"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Serialized Inventory"),
|
||||
"youtube_id": "gvOVlEwFDAk"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Batch Inventory"),
|
||||
"youtube_id": "J0QKl7ABPKM"
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Managing Subcontracting"),
|
||||
"youtube_id": "ThiMCC2DtKo"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -166,6 +166,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
from tabItem
|
||||
where tabItem.docstatus < 2
|
||||
and ifnull(tabItem.has_variants, 0)=0
|
||||
and tabItem.disabled=0
|
||||
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
|
||||
and (tabItem.`{key}` LIKE %(txt)s
|
||||
or tabItem.item_name LIKE %(txt)s
|
||||
@@ -215,7 +216,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
||||
return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
|
||||
from `tabDelivery Note`
|
||||
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
||||
`tabDelivery Note`.docstatus = 1 %(fcond)s and
|
||||
`tabDelivery Note`.docstatus = 1 and status not in ("Stopped", "Closed") %(fcond)s and
|
||||
(ifnull((select sum(qty) from `tabDelivery Note Item` where
|
||||
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
|
||||
ifnull((select sum(qty) from `tabSales Invoice Item` where
|
||||
@@ -303,10 +304,10 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
# Hence the first condition is an "OR"
|
||||
if not filters: filters = {}
|
||||
|
||||
condition = ""
|
||||
condition = ""
|
||||
if filters.get("company"):
|
||||
condition += "and tabAccount.company = %(company)s"
|
||||
|
||||
|
||||
return frappe.db.sql("""select tabAccount.name from `tabAccount`
|
||||
where (tabAccount.report_type = "Profit and Loss"
|
||||
or tabAccount.account_type in ("Income Account", "Temporary"))
|
||||
@@ -314,6 +315,6 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
and tabAccount.`{key}` LIKE %(txt)s
|
||||
{condition} {match_condition}"""
|
||||
.format(condition=condition, match_condition=get_match_cond(doctype), key=searchfield), {
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
'company': filters.get("company", "")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -163,7 +163,7 @@ def validate_recurring_document(doc):
|
||||
raise_exception=1)
|
||||
|
||||
elif not (doc.from_date and doc.to_date):
|
||||
throw(_("Period From and Period To dates mandatory for recurring %s") % doc.doctype)
|
||||
throw(_("Period From and Period To dates mandatory for recurring {0}").format(doc.doctype))
|
||||
|
||||
#
|
||||
def convert_to_recurring(doc, posting_date):
|
||||
|
||||
@@ -219,12 +219,12 @@ class SellingController(StockController):
|
||||
so_warehouse = so_item and so_item[0]["warehouse"] or ""
|
||||
return so_qty, so_warehouse
|
||||
|
||||
def check_stop_sales_order(self, ref_fieldname):
|
||||
def check_stop_or_close_sales_order(self, ref_fieldname):
|
||||
for d in self.get("items"):
|
||||
if d.get(ref_fieldname):
|
||||
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
|
||||
if status == "Stopped":
|
||||
frappe.throw(_("Sales Order {0} is stopped").format(d.get(ref_fieldname)))
|
||||
if status in ("Stopped", "Closed"):
|
||||
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
|
||||
|
||||
def check_active_sales_items(obj):
|
||||
for d in obj.get("items"):
|
||||
|
||||
@@ -37,6 +37,7 @@ status_map = {
|
||||
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Stopped", "eval:self.status=='Stopped'"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
],
|
||||
"Purchase Order": [
|
||||
["Draft", None],
|
||||
@@ -44,18 +45,22 @@ status_map = {
|
||||
["To Bill", "eval:self.per_received == 100 and self.per_billed < 100 and self.docstatus == 1"],
|
||||
["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Delivered", "eval:self.status=='Delivered'"],
|
||||
["Stopped", "eval:self.status=='Stopped'"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
],
|
||||
"Delivery Note": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
],
|
||||
"Purchase Receipt": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
]
|
||||
}
|
||||
|
||||
@@ -71,12 +76,16 @@ class StatusUpdater(Document):
|
||||
self.update_qty()
|
||||
self.validate_qty()
|
||||
|
||||
def set_status(self, update=False):
|
||||
def set_status(self, update=False, status=None):
|
||||
if self.is_new():
|
||||
return
|
||||
|
||||
if self.doctype in status_map:
|
||||
_status = self.status
|
||||
|
||||
if status and update:
|
||||
self.db_set("status", status)
|
||||
|
||||
sl = status_map[self.doctype][:]
|
||||
sl.reverse()
|
||||
for s in sl:
|
||||
@@ -168,7 +177,6 @@ class StatusUpdater(Document):
|
||||
else:
|
||||
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
|
||||
|
||||
args['set_modified'] = ''
|
||||
if change_modified:
|
||||
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
|
||||
.format(frappe.db.escape(frappe.session.user))
|
||||
@@ -252,9 +260,9 @@ class StatusUpdater(Document):
|
||||
zero_amount_refdoc.append(item.get(ref_fieldname))
|
||||
|
||||
if zero_amount_refdoc:
|
||||
self.update_biling_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
||||
self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
||||
|
||||
def update_biling_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
||||
def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
||||
for ref_dn in zero_amount_refdoc:
|
||||
ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item`
|
||||
where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])
|
||||
|
||||
@@ -29,8 +29,8 @@ blogs.
|
||||
"""
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "6.5.2"
|
||||
github_link = "https://github.com/frappe/erpnext"
|
||||
app_version = "6.7.3"
|
||||
source_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);
|
||||
|
||||
@@ -78,25 +78,22 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "leave_type",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Leave Type",
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "leave_type",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Leave Type",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
@@ -126,62 +123,38 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Posting Date",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldname": "leave_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Fiscal Year",
|
||||
"in_list_view": 1,
|
||||
"label": "Leave Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "fiscal_year",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "Fiscal Year",
|
||||
"oldfieldname": "leave_type",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Leave Type",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
@@ -195,19 +168,20 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "carry_forward",
|
||||
"fieldtype": "Check",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Carry Forward",
|
||||
"label": "From Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
@@ -216,18 +190,39 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "carry_forward",
|
||||
"fieldname": "carry_forwarded_leaves",
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Carry Forwarded Leaves",
|
||||
"label": "To Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
@@ -255,6 +250,50 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "",
|
||||
"fieldname": "carry_forward",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Add unused leaves from previous allocations",
|
||||
"no_copy": 0,
|
||||
"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,
|
||||
"collapsible": 0,
|
||||
"depends_on": "carry_forward",
|
||||
"fieldname": "carry_forwarded_leaves",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Unused leaves",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
@@ -310,7 +349,7 @@
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-10-02 07:38:55.314632",
|
||||
"modified": "2015-11-04 03:13:11.121463",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Allocation",
|
||||
@@ -359,7 +398,7 @@
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "employee,employee_name,leave_type,total_leaves_allocated,fiscal_year",
|
||||
"search_fields": "employee,employee_name,leave_type,total_leaves_allocated",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import cint, flt
|
||||
from frappe.utils import cint, flt, date_diff
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.utils import set_employee_name
|
||||
|
||||
class LeaveAllocation(Document):
|
||||
def validate(self):
|
||||
self.validate_period()
|
||||
self.validate_new_leaves_allocated_value()
|
||||
self.check_existing_leave_allocation()
|
||||
if not self.total_leaves_allocated:
|
||||
@@ -23,6 +24,10 @@ class LeaveAllocation(Document):
|
||||
def on_update(self):
|
||||
self.get_total_allocated_leaves()
|
||||
|
||||
def validate_period(self):
|
||||
if date_diff(self.to_date, self.from_date) <= 0:
|
||||
frappe.throw(_("Invalid period"))
|
||||
|
||||
def validate_new_leaves_allocated_value(self):
|
||||
"""validate that leave allocation is in multiples of 0.5"""
|
||||
if flt(self.new_leaves_allocated) % 0.5:
|
||||
@@ -31,28 +36,29 @@ class LeaveAllocation(Document):
|
||||
def check_existing_leave_allocation(self):
|
||||
"""check whether leave for same type is already allocated or not"""
|
||||
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
|
||||
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
|
||||
(self.employee, self.leave_type, self.fiscal_year))
|
||||
where employee='%s' and leave_type='%s' and to_date >= '%s' and from_date <= '%s' and docstatus=1
|
||||
"""%(self.employee, self.leave_type, self.from_date, self.to_date))
|
||||
|
||||
if leave_allocation:
|
||||
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for Fiscal Year {0}").format(self.leave_type,
|
||||
self.employee, self.fiscal_year))
|
||||
frappe.throw('<a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
|
||||
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for period {2} - {3}").format(self.leave_type,
|
||||
self.employee, self.from_date, self.to_date))
|
||||
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
|
||||
|
||||
def get_leave_bal(self, prev_fyear):
|
||||
return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear)
|
||||
def get_leave_bal(self):
|
||||
return self.get_leaves_allocated() - self.get_leaves_applied()
|
||||
|
||||
def get_leaves_applied(self, fiscal_year):
|
||||
def get_leaves_applied(self):
|
||||
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
|
||||
from `tabLeave Application` where employee=%s and leave_type=%s
|
||||
and fiscal_year=%s and docstatus=1""",
|
||||
(self.employee, self.leave_type, fiscal_year))
|
||||
and to_date<=%s and docstatus=1""",
|
||||
(self.employee, self.leave_type, self.from_date))
|
||||
return leaves_applied and flt(leaves_applied[0][0]) or 0
|
||||
|
||||
def get_leaves_allocated(self, fiscal_year):
|
||||
def get_leaves_allocated(self):
|
||||
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0))
|
||||
from `tabLeave Allocation` where employee=%s and leave_type=%s
|
||||
and fiscal_year=%s and docstatus=1 and name!=%s""",
|
||||
(self.employee, self.leave_type, fiscal_year, self.name))
|
||||
and to_date<=%s and docstatus=1 and name!=%s""",
|
||||
(self.employee, self.leave_type, self.from_date, self.name))
|
||||
return leaves_allocated and flt(leaves_allocated[0][0]) or 0
|
||||
|
||||
def allow_carry_forward(self):
|
||||
@@ -67,14 +73,11 @@ class LeaveAllocation(Document):
|
||||
def get_carry_forwarded_leaves(self):
|
||||
if self.carry_forward:
|
||||
self.allow_carry_forward()
|
||||
prev_fiscal_year = frappe.db.sql("""select name from `tabFiscal Year`
|
||||
where year_start_date = (select date_add(year_start_date, interval -1 year)
|
||||
from `tabFiscal Year` where name=%s)
|
||||
order by name desc limit 1""", self.fiscal_year)
|
||||
prev_fiscal_year = prev_fiscal_year and prev_fiscal_year[0][0] or ''
|
||||
|
||||
prev_bal = 0
|
||||
if prev_fiscal_year and cint(self.carry_forward) == 1:
|
||||
prev_bal = self.get_leave_bal(prev_fiscal_year)
|
||||
if cint(self.carry_forward) == 1:
|
||||
prev_bal = self.get_leave_bal()
|
||||
|
||||
ret = {
|
||||
'carry_forwarded_leaves': prev_bal,
|
||||
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
|
||||
@@ -83,6 +86,10 @@ class LeaveAllocation(Document):
|
||||
|
||||
def get_total_allocated_leaves(self):
|
||||
leave_det = self.get_carry_forwarded_leaves()
|
||||
self.validate_total_leaves_allocated(leave_det)
|
||||
frappe.db.set(self,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves']))
|
||||
frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
|
||||
|
||||
def validate_total_leaves_allocated(self, leave_det):
|
||||
if date_diff(self.to_date, self.from_date) <= leave_det['total_leaves_allocated']:
|
||||
frappe.throw(_("Total allocated leaves are more than period"))
|
||||
|
||||
@@ -1,4 +1,69 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import getdate
|
||||
|
||||
test_records = frappe.get_test_records('Leave Allocation')
|
||||
class TestLeaveAllocation(unittest.TestCase):
|
||||
def test_overlapping_allocation(self):
|
||||
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
|
||||
leaves = [
|
||||
{
|
||||
"doctype": "Leave Allocation",
|
||||
"__islocal": 1,
|
||||
"employee": employee.name,
|
||||
"employee_name": employee.employee_name,
|
||||
"leave_type": "_Test Leave Type",
|
||||
"from_date": getdate("2015-10-1"),
|
||||
"to_date": getdate("2015-10-31"),
|
||||
"new_leaves_allocated": 5,
|
||||
"docstatus": 1
|
||||
},
|
||||
{
|
||||
"doctype": "Leave Allocation",
|
||||
"__islocal": 1,
|
||||
"employee": employee.name,
|
||||
"employee_name": employee.employee_name,
|
||||
"leave_type": "_Test Leave Type",
|
||||
"from_date": getdate("2015-09-1"),
|
||||
"to_date": getdate("2015-11-30"),
|
||||
"new_leaves_allocated": 5
|
||||
}
|
||||
]
|
||||
|
||||
frappe.get_doc(leaves[0]).save()
|
||||
self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save)
|
||||
|
||||
def test_invalid_period(self):
|
||||
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
|
||||
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Leave Allocation",
|
||||
"__islocal": 1,
|
||||
"employee": employee.name,
|
||||
"employee_name": employee.employee_name,
|
||||
"leave_type": "_Test Leave Type",
|
||||
"from_date": getdate("2015-09-30"),
|
||||
"to_date": getdate("2015-09-1"),
|
||||
"new_leaves_allocated": 5
|
||||
})
|
||||
|
||||
#invalid period
|
||||
self.assertRaises(frappe.ValidationError, d.save)
|
||||
|
||||
def test_allocated_leave_days_over_period(self):
|
||||
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Leave Allocation",
|
||||
"__islocal": 1,
|
||||
"employee": employee.name,
|
||||
"employee_name": employee.employee_name,
|
||||
"leave_type": "_Test Leave Type",
|
||||
"from_date": getdate("2015-09-1"),
|
||||
"to_date": getdate("2015-09-30"),
|
||||
"new_leaves_allocated": 35
|
||||
})
|
||||
|
||||
#allocated leave more than period
|
||||
self.assertRaises(frappe.ValidationError, d.save)
|
||||
|
||||
test_dependencies = ["Employee", "Leave Type"]
|
||||
@@ -1,18 +1,20 @@
|
||||
[
|
||||
{
|
||||
"docstatus": 1,
|
||||
"doctype": "Leave Allocation",
|
||||
"employee": "_T-Employee-0001",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"leave_type": "_Test Leave Type",
|
||||
"new_leaves_allocated": 15
|
||||
},
|
||||
{
|
||||
"docstatus": 1,
|
||||
"doctype": "Leave Allocation",
|
||||
"employee": "_T-Employee-0002",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"leave_type": "_Test Leave Type",
|
||||
"new_leaves_allocated": 15
|
||||
}
|
||||
]
|
||||
{
|
||||
"docstatus": 1,
|
||||
"doctype": "Leave Allocation",
|
||||
"employee": "_T-Employee-0001",
|
||||
"from_date": "2013-01-01",
|
||||
"to_date": "2013-12-31",
|
||||
"leave_type": "_Test Leave Type",
|
||||
"new_leaves_allocated": 15
|
||||
},
|
||||
{
|
||||
"docstatus": 1,
|
||||
"doctype": "Leave Allocation",
|
||||
"employee": "_T-Employee-0002",
|
||||
"from_date": "2013-01-01",
|
||||
"to_date": "2013-12-31",
|
||||
"leave_type": "_Test Leave Type",
|
||||
"new_leaves_allocated": 15
|
||||
}
|
||||
]
|
||||
@@ -2,114 +2,101 @@
|
||||
// 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));
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
cur_frm.set_query("leave_approver", function() {
|
||||
return {
|
||||
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
||||
filters: {
|
||||
employee: cur_frm.doc.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);
|
||||
}
|
||||
frappe.ui.form.on("Leave Application", {
|
||||
onload: function(frm) {
|
||||
if (!frm.doc.posting_date) {
|
||||
frm.set_value("posting_date", get_today());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.employee = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.fiscal_year = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.leave_type = function (doc, dt, dn){
|
||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
frm.set_query("leave_approver", function() {
|
||||
return {
|
||||
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
||||
filters: {
|
||||
employee: frm.doc.employee
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.set_query("employee", erpnext.queries.employee);
|
||||
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if (frm.is_new()) {
|
||||
frm.set_value("status", "Open");
|
||||
frm.trigger("calculate_total_days");
|
||||
}
|
||||
},
|
||||
|
||||
leave_approver: function(frm) {
|
||||
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
|
||||
},
|
||||
|
||||
employee: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
},
|
||||
|
||||
leave_type: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
},
|
||||
|
||||
half_day: function(frm) {
|
||||
if (frm.doc.from_date) {
|
||||
frm.set_value("to_date", frm.doc.from_date);
|
||||
frm.trigger("calculate_total_days");
|
||||
}
|
||||
},
|
||||
|
||||
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");
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
frm.trigger("calculate_total_days");
|
||||
},
|
||||
|
||||
get_leave_balance: function(frm) {
|
||||
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
|
||||
return frm.call({
|
||||
method: "get_leave_balance",
|
||||
args: {
|
||||
employee: frm.doc.employee,
|
||||
from_date: frm.doc.from_date,
|
||||
to_date: frm.doc.to_date,
|
||||
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);
|
||||
frm.trigger("get_leave_balance");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "Open\nApproved\nRejected",
|
||||
"permlevel": 1,
|
||||
@@ -559,7 +559,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 3,
|
||||
"modified": "2015-10-02 07:38:55.471712",
|
||||
"modified": "2015-10-28 16:14:25.640730",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Application",
|
||||
|
||||
@@ -100,7 +100,7 @@ class LeaveApplication(Document):
|
||||
|
||||
if not is_lwp(self.leave_type):
|
||||
self.leave_balance = get_leave_balance(self.employee,
|
||||
self.leave_type, self.fiscal_year)["leave_balance"]
|
||||
self.leave_type, self.from_date, self.to_date)["leave_balance"]
|
||||
|
||||
if self.status != "Rejected" \
|
||||
and self.leave_balance - self.total_leave_days < 0:
|
||||
@@ -122,9 +122,8 @@ class LeaveApplication(Document):
|
||||
employee = %(employee)s
|
||||
and docstatus < 2
|
||||
and status in ("Open", "Approved")
|
||||
and (from_date between %(from_date)s and %(to_date)s
|
||||
or to_date between %(from_date)s and %(to_date)s
|
||||
or %(from_date)s between from_date and to_date)
|
||||
and to_date >= %(from_date)s
|
||||
and from_date <= %(to_date)s
|
||||
and name != %(name)s""", {
|
||||
"employee": self.employee,
|
||||
"from_date": self.from_date,
|
||||
@@ -251,18 +250,18 @@ def get_total_leave_days(leave_app):
|
||||
return ret
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_leave_balance(employee, leave_type, fiscal_year):
|
||||
def get_leave_balance(employee, leave_type, from_date, to_date):
|
||||
leave_all = frappe.db.sql("""select total_leaves_allocated
|
||||
from `tabLeave Allocation` where employee = %s and leave_type = %s
|
||||
and fiscal_year = %s and docstatus = 1""", (employee,
|
||||
leave_type, fiscal_year))
|
||||
and from_date<=%s and to_date>=%s and docstatus = 1""", (employee,
|
||||
leave_type, from_date, to_date))
|
||||
|
||||
leave_all = leave_all and flt(leave_all[0][0]) or 0
|
||||
|
||||
leave_app = frappe.db.sql("""select SUM(total_leave_days)
|
||||
from `tabLeave Application`
|
||||
where employee = %s and leave_type = %s and fiscal_year = %s
|
||||
and status="Approved" and docstatus = 1""", (employee, leave_type, fiscal_year))
|
||||
where employee = %s and leave_type = %s and to_date>=%s and from_date<=%s
|
||||
and status="Approved" and docstatus = 1""", (employee, leave_type, from_date, to_date))
|
||||
leave_app = leave_app and flt(leave_app[0][0]) or 0
|
||||
|
||||
ret = {'leave_balance': leave_all - leave_app}
|
||||
|
||||
@@ -145,16 +145,38 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Fiscal Year",
|
||||
"label": "From Date",
|
||||
"no_copy": 0,
|
||||
"options": "Fiscal Year",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "To Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -260,7 +282,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"modified": "2015-06-05 11:38:19.994852",
|
||||
"modified": "2015-10-28 16:23:57.733900",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Control Panel",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cint, cstr, flt, nowdate, comma_and
|
||||
from frappe.utils import cint, cstr, flt, nowdate, comma_and, date_diff
|
||||
from frappe import msgprint, _
|
||||
from frappe.model.document import Document
|
||||
|
||||
@@ -27,9 +27,13 @@ class LeaveControlPanel(Document):
|
||||
return e
|
||||
|
||||
def validate_values(self):
|
||||
for f in ["fiscal_year", "leave_type", "no_of_days"]:
|
||||
for f in ["from_date", "to_date", "leave_type", "no_of_days"]:
|
||||
if not self.get(f):
|
||||
frappe.throw(_("{0} is required").format(self.meta.get_label(f)))
|
||||
|
||||
def to_date_validation(self):
|
||||
if date_diff(self.to_date, self.from_date) <= 0:
|
||||
return "Invalid period"
|
||||
|
||||
def allocate_leave(self):
|
||||
self.validate_values()
|
||||
@@ -45,8 +49,8 @@ class LeaveControlPanel(Document):
|
||||
la.employee = cstr(d[0])
|
||||
la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
|
||||
la.leave_type = self.leave_type
|
||||
la.fiscal_year = self.fiscal_year
|
||||
la.posting_date = nowdate()
|
||||
la.from_date = self.from_date
|
||||
la.to_date = self.to_date
|
||||
la.carry_forward = cint(self.carry_forward)
|
||||
la.new_leaves_allocated = flt(self.no_of_days)
|
||||
la.docstatus = 1
|
||||
|
||||
@@ -25,8 +25,8 @@ cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) {
|
||||
|
||||
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
|
||||
cur_frm.cscript.display_activity_log("");
|
||||
var check = confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]));
|
||||
if(check){
|
||||
|
||||
frappe.confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]), function() {
|
||||
// clear all in locals
|
||||
if(locals["Salary Slip"]) {
|
||||
$.each(locals["Salary Slip"], function(name, d) {
|
||||
@@ -40,7 +40,7 @@ cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
|
||||
}
|
||||
|
||||
return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
frappe.query_reports["Employee Leave Balance"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"fiscal_year",
|
||||
"label": __("Fiscal Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year")
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.year_start()
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.year_end()
|
||||
},
|
||||
{
|
||||
"fieldname":"company",
|
||||
|
||||
@@ -24,52 +24,47 @@ def execute(filters=None):
|
||||
|
||||
leave_types = frappe.db.sql_list("select name from `tabLeave Type`")
|
||||
|
||||
if filters.get("fiscal_year"):
|
||||
fiscal_years = [filters["fiscal_year"]]
|
||||
else:
|
||||
fiscal_years = frappe.db.sql_list("select name from `tabFiscal Year` order by name desc")
|
||||
|
||||
employee_names = [d.name for d in employees]
|
||||
|
||||
allocations = frappe.db.sql("""select employee, fiscal_year, leave_type, total_leaves_allocated
|
||||
allocations = frappe.db.sql("""select employee, leave_type, sum(new_leaves_allocated) as leaves_allocated
|
||||
from `tabLeave Allocation`
|
||||
where docstatus=1 and employee in (%s)""" %
|
||||
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
|
||||
|
||||
applications = frappe.db.sql("""select employee, fiscal_year, leave_type,
|
||||
where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
|
||||
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
|
||||
filters.get("to_date")), employee_names, as_dict=True)
|
||||
|
||||
applications = frappe.db.sql("""select employee, leave_type,
|
||||
SUM(total_leave_days) as leaves
|
||||
from `tabLeave Application`
|
||||
where status="Approved" and docstatus = 1 and employee in (%s)
|
||||
group by employee, fiscal_year, leave_type""" %
|
||||
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
|
||||
and from_date >= '%s' and to_date <= '%s'
|
||||
group by employee, leave_type""" %
|
||||
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
|
||||
filters.get("to_date")), employee_names, as_dict=True)
|
||||
|
||||
columns = [
|
||||
_("Fiscal Year"), _("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150"
|
||||
_("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150"
|
||||
]
|
||||
|
||||
for leave_type in leave_types:
|
||||
columns.append(_(leave_type) + " " + _("Allocated") + ":Float")
|
||||
columns.append(_(leave_type) + " " + _("Opening") + ":Float")
|
||||
columns.append(_(leave_type) + " " + _("Taken") + ":Float")
|
||||
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
|
||||
|
||||
data = {}
|
||||
for d in allocations:
|
||||
data.setdefault((d.fiscal_year, d.employee,
|
||||
d.leave_type), frappe._dict()).allocation = d.total_leaves_allocated
|
||||
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
|
||||
|
||||
for d in applications:
|
||||
data.setdefault((d.fiscal_year, d.employee,
|
||||
d.leave_type), frappe._dict()).leaves = d.leaves
|
||||
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
|
||||
|
||||
result = []
|
||||
for fiscal_year in fiscal_years:
|
||||
for employee in employees:
|
||||
row = [fiscal_year, employee.name, employee.employee_name, employee.department]
|
||||
result.append(row)
|
||||
for leave_type in leave_types:
|
||||
tmp = data.get((fiscal_year, employee.name, leave_type), frappe._dict())
|
||||
row.append(tmp.allocation or 0)
|
||||
row.append(tmp.leaves or 0)
|
||||
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
|
||||
for employee in employees:
|
||||
row = [employee.name, employee.employee_name, employee.department]
|
||||
result.append(row)
|
||||
for leave_type in leave_types:
|
||||
tmp = data.get((employee.name, leave_type), frappe._dict())
|
||||
row.append(tmp.allocation or 0)
|
||||
row.append(tmp.leaves or 0)
|
||||
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
|
||||
|
||||
return columns, result
|
||||
|
||||
@@ -83,7 +83,7 @@ erpnext.production_order = {
|
||||
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'],
|
||||
"icon-exclamation", "btn-default");
|
||||
} else if (doc.status == 'Stopped') {
|
||||
frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Production Order'],
|
||||
frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Production Order'],
|
||||
"icon-check", "btn-default");
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ erpnext.production_order = {
|
||||
} else msgprint(__("Please enter Production Item first"));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
set_default_warehouse: function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.production_order.production_order.get_default_warehouse",
|
||||
@@ -239,18 +239,11 @@ $.extend(cur_frm.cscript, {
|
||||
});
|
||||
|
||||
cur_frm.cscript['Stop Production Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm(__("Do you really want to stop production order: " + doc.name));
|
||||
if (check) {
|
||||
return $c_obj(doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
|
||||
}
|
||||
$c_obj(cur_frm.doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Unstop Production Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm(__("Do really want to unstop production order: " + doc.name));
|
||||
if (check)
|
||||
return $c_obj(doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
|
||||
$c_obj(cur_frm.doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Transfer Raw Materials'] = function() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import flt, get_datetime, getdate, date_diff, cint
|
||||
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||
@@ -159,22 +159,22 @@ class ProductionOrder(Document):
|
||||
|
||||
def on_cancel(self):
|
||||
self.validate_cancel()
|
||||
|
||||
|
||||
frappe.db.set(self,'status', 'Cancelled')
|
||||
self.update_planned_qty()
|
||||
self.delete_time_logs()
|
||||
|
||||
|
||||
def validate_cancel(self):
|
||||
if self.status == "Stopped":
|
||||
frappe.throw(_("Stopped Production Order cannot be cancelled, Unstop it first to cancel"))
|
||||
|
||||
|
||||
# Check whether any stock entry exists against this Production Order
|
||||
stock_entry = frappe.db.sql("""select name from `tabStock Entry`
|
||||
where production_order = %s and docstatus = 1""", self.name)
|
||||
if stock_entry:
|
||||
frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0]))
|
||||
|
||||
def update_planned_qty(self):
|
||||
def update_planned_qty(self):
|
||||
update_bin_qty(self.production_item, self.fg_warehouse, {
|
||||
"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)
|
||||
})
|
||||
@@ -342,8 +342,8 @@ class ProductionOrder(Document):
|
||||
@frappe.whitelist()
|
||||
def get_item_details(item):
|
||||
res = frappe.db.sql("""select stock_uom, description
|
||||
from `tabItem` where (ifnull(end_of_life, "0000-00-00")="0000-00-00" or end_of_life > now())
|
||||
and name=%s""", item, as_dict=1)
|
||||
from `tabItem` where disabled=0 and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
|
||||
and name=%s""", (nowdate(), item), as_dict=1)
|
||||
if not res:
|
||||
return {}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
self.assertEqual(prod_order.name, time_log.production_order)
|
||||
self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty)
|
||||
self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours)
|
||||
|
||||
|
||||
manufacturing_settings = frappe.get_doc({
|
||||
"doctype": "Manufacturing Settings",
|
||||
"allow_production_on_holidays": 0
|
||||
@@ -136,6 +136,11 @@ class TestProductionOrder(unittest.TestCase):
|
||||
self.assertRaises(frappe.ValidationError, prod_order.save)
|
||||
|
||||
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", None)
|
||||
frappe.db.set_value("Item", "_Test FG Item", "disabled", 1)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, prod_order.save)
|
||||
|
||||
frappe.db.set_value("Item", "_Test FG Item", "disabled", 0)
|
||||
|
||||
prod_order = make_prod_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True)
|
||||
self.assertRaises(ItemHasVariantError, prod_order.save)
|
||||
|
||||
@@ -224,8 +224,10 @@ 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
|
||||
execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate")
|
||||
erpnext.patches.v6_6.fix_website_image
|
||||
erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation
|
||||
|
||||
@@ -9,7 +9,9 @@ def execute():
|
||||
if outgoing and outgoing['mail_server'] and outgoing['mail_login']:
|
||||
account = frappe.new_doc("Email Account")
|
||||
mapping = {
|
||||
"email_id": "mail_login",
|
||||
"login_id_is_different": 1,
|
||||
"email_id": "auto_email_id",
|
||||
"login_id": "mail_login",
|
||||
"password": "mail_password",
|
||||
"footer": "footer",
|
||||
"smtp_server": "mail_server",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
@@ -11,9 +13,12 @@ def execute():
|
||||
for m in frappe.get_all("Project Milestone", "*"):
|
||||
if (m.milestone and m.milestone_date
|
||||
and frappe.db.exists("Project", m.parent)):
|
||||
subject = (m.milestone[:139] + "…") if (len(m.milestone) > 140) else m.milestone
|
||||
description = m.milestone
|
||||
task = frappe.get_doc({
|
||||
"doctype": "Task",
|
||||
"subject": m.milestone,
|
||||
"subject": subject,
|
||||
"description": description if description!=subject else None,
|
||||
"expected_start_date": m.milestone_date,
|
||||
"status": "Open" if m.status=="Pending" else "Closed",
|
||||
"project": m.parent,
|
||||
|
||||
@@ -40,7 +40,17 @@ def fix_files_for_item(files_path, unlinked_files):
|
||||
file_data = frappe.get_doc("File", unlinked_files[file_url]["file"])
|
||||
file_data.attached_to_doctype = "Item"
|
||||
file_data.attached_to_name = item_code
|
||||
file_data.save()
|
||||
file_data.flags.ignore_folder_validate = True
|
||||
|
||||
try:
|
||||
file_data.save()
|
||||
except IOError:
|
||||
print "File {0} does not exist".format(new_file_url)
|
||||
|
||||
# marking fix to prevent further errors
|
||||
fixed_files.append(file_url)
|
||||
|
||||
continue
|
||||
|
||||
# set it as image in Item
|
||||
if not frappe.db.get_value("Item", item_code, "image"):
|
||||
|
||||
@@ -30,3 +30,6 @@ def execute():
|
||||
|
||||
frappe.reload_doctype("Sales Invoice")
|
||||
frappe.db.sql("""update `tabSales Invoice` set title = customer_name""")
|
||||
|
||||
frappe.reload_doctype("Expense Claim")
|
||||
frappe.db.sql("""update `tabExpense Claim` set title = employee_name""")
|
||||
|
||||
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()
|
||||
|
||||
1
erpnext/patches/v6_6/__init__.py
Normal file
1
erpnext/patches/v6_6/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
32
erpnext/patches/v6_6/fix_website_image.py
Normal file
32
erpnext/patches/v6_6/fix_website_image.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import encode
|
||||
|
||||
def execute():
|
||||
"""Fix the File records created via item.py even if the website_image file didn't exist"""
|
||||
for item in frappe.db.sql_list("""select name from `tabItem`
|
||||
where website_image is not null and website_image != ''
|
||||
and website_image like '/files/%'
|
||||
and exists (
|
||||
select name from `tabFile`
|
||||
where attached_to_doctype='Item'
|
||||
and attached_to_name=`tabItem`.name
|
||||
and file_url=`tabItem`.website_image
|
||||
and (file_name is null or file_name = '')
|
||||
)"""):
|
||||
|
||||
item = frappe.get_doc("Item", item)
|
||||
file = frappe.get_doc("File", {
|
||||
"attached_to_doctype": "Item",
|
||||
"attached_to_name": item.name,
|
||||
"file_url": item.website_image
|
||||
})
|
||||
|
||||
try:
|
||||
file.validate_file()
|
||||
except IOError:
|
||||
print encode(item.website_image), "does not exist"
|
||||
file.delete()
|
||||
item.db_set("website_image", None, update_modified=False)
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
if frappe.db.has_column("Leave Allocation", "fiscal_year"):
|
||||
for leave_allocation in frappe.db.sql("select name, fiscal_year from `tabLeave Allocation`", as_dict=True):
|
||||
dates = frappe.db.get_value("Fiscal Year", leave_allocation["fiscal_year"],
|
||||
["year_start_date", "year_end_date"])
|
||||
|
||||
if dates:
|
||||
year_start_date, year_end_date = dates
|
||||
|
||||
frappe.db.sql("""update `tabLeave Allocation`
|
||||
set from_date=%s, to_date=%s where name=%s""",
|
||||
(year_start_date, year_end_date, leave_allocation["name"]))
|
||||
|
||||
@@ -28,7 +28,8 @@ class TimeLogBatch(Document):
|
||||
d.update({
|
||||
"hours": tl.hours,
|
||||
"activity_type": tl.activity_type,
|
||||
"billing_amount": tl.billing_amount
|
||||
"billing_amount": tl.billing_amount,
|
||||
"note": tl.note
|
||||
})
|
||||
|
||||
def validate_time_log_is_submitted(self, tl):
|
||||
|
||||
@@ -115,6 +115,27 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "note",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Note",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
@@ -133,4 +154,4 @@
|
||||
"permissions": [],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
}
|
||||
|
||||
BIN
erpnext/public/images/YouTube-icon-full_color.png
Normal file
BIN
erpnext/public/images/YouTube-icon-full_color.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
erpnext/public/images/erpnext-video-placeholder.jpg
Normal file
BIN
erpnext/public/images/erpnext-video-placeholder.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
@@ -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,
|
||||
@@ -70,10 +76,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
|
||||
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
|
||||
this.apply_default_taxes();
|
||||
}
|
||||
|
||||
if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"] && !this.frm.doc.is_pos) {
|
||||
this.calculate_taxes_and_totals();
|
||||
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
|
||||
&& !this.frm.doc.is_pos) {
|
||||
me.calculate_taxes_and_totals();
|
||||
}
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||
cur_frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
@@ -96,7 +101,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
me.frm.doc.name);
|
||||
|
||||
if(taxes_and_charges_field) {
|
||||
frappe.call({
|
||||
return frappe.call({
|
||||
method: "erpnext.controllers.accounts_controller.get_default_taxes_and_charges",
|
||||
args: {
|
||||
"master_doctype": taxes_and_charges_field.options
|
||||
@@ -104,6 +109,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
me.frm.set_value("taxes", r.message);
|
||||
me.calculate_taxes_and_totals();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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({
|
||||
@@ -350,7 +362,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
get_exchange_rate: function(from_currency, to_currency, callback) {
|
||||
frappe.call({
|
||||
return frappe.call({
|
||||
method: "erpnext.setup.utils.get_exchange_rate",
|
||||
args: {
|
||||
from_currency: from_currency,
|
||||
@@ -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
|
||||
@@ -723,7 +735,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
var valid = true;
|
||||
|
||||
$.each(["company", "customer"], function(i, fieldname) {
|
||||
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname)) {
|
||||
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname && me.frm.doc.doctype != "Purchase Order")) {
|
||||
if (!me.frm.doc[fieldname]) {
|
||||
msgprint(__("Please specify") + ": " +
|
||||
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
|
||||
|
||||
@@ -7,7 +7,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||
method = "erpnext.accounts.party.get_party_details";
|
||||
}
|
||||
if(!args) {
|
||||
if(frm.doc.customer) {
|
||||
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
|
||||
args = {
|
||||
party: frm.doc.customer,
|
||||
party_type: "Customer",
|
||||
@@ -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;
|
||||
@@ -48,7 +53,7 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
|
||||
if(frm.updating_party_details) return;
|
||||
|
||||
if(!address_field) {
|
||||
if(frm.doc.customer) {
|
||||
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
|
||||
address_field = "customer_address";
|
||||
} else if(frm.doc.supplier) {
|
||||
address_field = "supplier_address";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ erpnext.selling.InstallationNote = frappe.ui.form.Controller.extend({
|
||||
source_doctype: "Delivery Note",
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
status: ["not in", ["Stopped", "Closed"]],
|
||||
per_installed: ["<", 99.99],
|
||||
customer: cur_frm.doc.customer || undefined,
|
||||
company: cur_frm.doc.company
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -15,45 +15,66 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
this.frm.dashboard.reset();
|
||||
var is_delivered_by_supplier = false;
|
||||
var is_delivery_note = false;
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
if(doc.status != 'Stopped') {
|
||||
if(doc.status != 'Stopped' && doc.status != 'Closed') {
|
||||
|
||||
// cur_frm.dashboard.add_progress(cint(doc.per_delivered) + __("% Delivered"),
|
||||
// doc.per_delivered);
|
||||
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
||||
// doc.per_billed);
|
||||
$.each(cur_frm.doc.items, function(i, item){
|
||||
if(item.delivered_by_supplier == 1 || item.supplier){
|
||||
if(item.qty > flt(item.ordered_qty))
|
||||
is_delivered_by_supplier = true;
|
||||
}
|
||||
else{
|
||||
if(item.qty > flt(item.delivered_qty))
|
||||
is_delivery_note = true;
|
||||
}
|
||||
})
|
||||
|
||||
// indent
|
||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||
cur_frm.add_custom_button(__('Material Request'), this.make_material_request);
|
||||
// material request
|
||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1
|
||||
&& flt(doc.per_delivered, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Material Request'), this.make_material_request);
|
||||
}
|
||||
|
||||
// make purchase order
|
||||
if(flt(doc.per_delivered, 2) < 100 && is_delivered_by_supplier) {
|
||||
cur_frm.add_custom_button(__('Purchase Order'), cur_frm.cscript.make_purchase_order);
|
||||
}
|
||||
|
||||
if(flt(doc.per_billed)==0) {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||
}
|
||||
|
||||
// stop
|
||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'])
|
||||
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
|
||||
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
|
||||
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
|
||||
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
|
||||
}
|
||||
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
}
|
||||
cur_frm.add_custom_button(__('Close'), this.close_sales_order)
|
||||
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
|
||||
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
|
||||
}
|
||||
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && is_delivery_note) {
|
||||
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
|
||||
}
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// un-stop
|
||||
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Sales Order']);
|
||||
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,15 +157,59 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
|
||||
make_bank_entry: function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_order",
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
|
||||
args: {
|
||||
"sales_order": cur_frm.doc.name
|
||||
"dt": "Sales Order",
|
||||
"dn": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
},
|
||||
make_purchase_order: function(){
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("For Supplier"),
|
||||
fields: [
|
||||
{"fieldtype": "Link", "label": __("Supplier"), "fieldname": "supplier", "options":"Supplier",
|
||||
"get_query": function () {
|
||||
return {
|
||||
query:"erpnext.selling.doctype.sales_order.sales_order.get_supplier",
|
||||
filters: {'parent': cur_frm.doc.name}
|
||||
}
|
||||
}, "reqd": 1 },
|
||||
{"fieldtype": "Button", "label": __("Make Purchase Order"), "fieldname": "make_purchase_order", "cssClass": "btn-primary"},
|
||||
]
|
||||
});
|
||||
|
||||
dialog.fields_dict.make_purchase_order.$input.click(function() {
|
||||
args = dialog.get_values();
|
||||
if(!args) return;
|
||||
dialog.hide();
|
||||
return frappe.call({
|
||||
type: "GET",
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order_for_drop_shipment",
|
||||
args: {
|
||||
"source_name": cur_frm.doc.name,
|
||||
"for_supplier": args.supplier
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
var doc = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
dialog.show();
|
||||
},
|
||||
stop_sales_order: function(){
|
||||
cur_frm.cscript.update_status("Stop", "Stopped")
|
||||
},
|
||||
close_sales_order: function(){
|
||||
cur_frm.cscript.update_status("Close", "Closed")
|
||||
}
|
||||
|
||||
});
|
||||
@@ -168,34 +233,23 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript['Stop Sales Order'] = function() {
|
||||
cur_frm.cscript.update_status = function(label, status){
|
||||
var doc = cur_frm.doc;
|
||||
|
||||
var check = confirm(__("Are you sure you want to STOP ") + doc.name);
|
||||
|
||||
if (check) {
|
||||
return $c('runserverobj', {
|
||||
'method':'stop_sales_order',
|
||||
'docs': doc
|
||||
}, function(r,rt) {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
}
|
||||
frappe.ui.form.is_saving = true;
|
||||
frappe.call({
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.update_status",
|
||||
args: {status: status, name: doc.name},
|
||||
callback: function(r){
|
||||
cur_frm.reload_doc();
|
||||
},
|
||||
always: function() {
|
||||
frappe.ui.form.is_saving = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Unstop Sales Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
|
||||
var check = confirm(__("Are you sure you want to UNSTOP ") + doc.name);
|
||||
|
||||
if (check) {
|
||||
return $c('runserverobj', {
|
||||
'method':'unstop_sales_order',
|
||||
'docs': doc
|
||||
}, function(r,rt) {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
}
|
||||
cur_frm.cscript.update_status('Re-open', 'Draft')
|
||||
}
|
||||
|
||||
cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Series",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -234,13 +234,14 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Sales",
|
||||
"depends_on": "",
|
||||
"fieldname": "order_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Order Type",
|
||||
"label": "Order Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "order_type",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -381,6 +382,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "po_no",
|
||||
"fieldtype": "Data",
|
||||
@@ -1134,7 +1136,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Apply Additional Discount On",
|
||||
"label": "Apply Additional Discount On",
|
||||
"no_copy": 0,
|
||||
"options": "\nGrand Total\nNet Total",
|
||||
"permlevel": 0,
|
||||
@@ -1808,7 +1810,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Source",
|
||||
"label": "Source",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "source",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -1973,11 +1975,11 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nDraft\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nStopped\nCancelled",
|
||||
"options": "\nDraft\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nStopped\nCancelled\nClosed",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
@@ -1998,7 +2000,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Delivery Status",
|
||||
"label": "Delivery Status",
|
||||
"no_copy": 1,
|
||||
"options": "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable",
|
||||
"permlevel": 0,
|
||||
@@ -2092,7 +2094,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Billing Status",
|
||||
"label": "Billing Status",
|
||||
"no_copy": 1,
|
||||
"options": "Not Billed\nFully Billed\nPartly Billed\nClosed",
|
||||
"permlevel": 0,
|
||||
@@ -2326,7 +2328,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Recurring Type",
|
||||
"label": "Recurring Type",
|
||||
"no_copy": 1,
|
||||
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
@@ -2553,7 +2555,7 @@
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-10-03 07:39:10.525609",
|
||||
"modified": "2015-10-22 16:32:34.339835",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -31,6 +31,7 @@ class SalesOrder(SellingController):
|
||||
self.validate_uom_is_integer("stock_uom", "qty")
|
||||
self.validate_for_items()
|
||||
self.validate_warehouse()
|
||||
self.validate_drop_ship()
|
||||
|
||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||
make_packing_list(self)
|
||||
@@ -67,7 +68,7 @@ class SalesOrder(SellingController):
|
||||
|
||||
if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or
|
||||
(self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \
|
||||
and not d.warehouse:
|
||||
and not d.warehouse and not cint(d.delivered_by_supplier):
|
||||
frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
|
||||
WarehouseRequired)
|
||||
|
||||
@@ -147,6 +148,11 @@ class SalesOrder(SellingController):
|
||||
doc.set_status(update=True)
|
||||
doc.update_opportunity()
|
||||
|
||||
def validate_drop_ship(self):
|
||||
for d in self.get('items'):
|
||||
if d.delivered_by_supplier and not d.supplier:
|
||||
frappe.throw(_("Row #{0}: Set Supplier for item {1}").format(d.idx, d.item_code))
|
||||
|
||||
def on_submit(self):
|
||||
super(SalesOrder, self).on_submit()
|
||||
|
||||
@@ -214,20 +220,13 @@ class SalesOrder(SellingController):
|
||||
if date_diff and date_diff[0][0]:
|
||||
frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name))
|
||||
|
||||
def stop_sales_order(self):
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
self.db_set('status', 'Stopped')
|
||||
self.set_status(update=True, status=status)
|
||||
self.update_reserved_qty()
|
||||
self.notify_update()
|
||||
clear_doctype_notifications(self)
|
||||
|
||||
def unstop_sales_order(self):
|
||||
self.check_modified_date()
|
||||
self.db_set('status', 'Draft')
|
||||
self.set_status(update=True)
|
||||
self.update_reserved_qty()
|
||||
clear_doctype_notifications(self)
|
||||
|
||||
def update_reserved_qty(self, so_item_rows=None):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
item_wh_list = []
|
||||
@@ -253,6 +252,46 @@ class SalesOrder(SellingController):
|
||||
def on_update(self):
|
||||
pass
|
||||
|
||||
def before_update_after_submit(self):
|
||||
self.validate_drop_ship()
|
||||
self.validate_supplier_after_submit()
|
||||
|
||||
def validate_supplier_after_submit(self):
|
||||
"""Check that supplier is the same after submit if PO is already made"""
|
||||
exc_list = []
|
||||
|
||||
for item in self.items:
|
||||
if item.supplier:
|
||||
supplier = frappe.db.get_value("Sales Order Item", {"parent": self.name, "item_code": item.item_code},
|
||||
"supplier")
|
||||
if item.ordered_qty > 0.0 and item.supplier != supplier:
|
||||
exc_list.append(_("Row #{0}: Not allowed to change Supplier as Purchase Order already exists").format(item.idx))
|
||||
|
||||
if exc_list:
|
||||
frappe.throw('\n'.join(exc_list))
|
||||
|
||||
def update_delivery_status(self, po_name):
|
||||
"""Update delivery status from Purchase Order for drop shipping"""
|
||||
tot_qty, delivered_qty = 0.0, 0.0
|
||||
|
||||
for item in self.items:
|
||||
if item.delivered_by_supplier:
|
||||
item_delivered_qty = frappe.db.sql("""select qty
|
||||
from `tabPurchase Order Item` poi, `tabPurchase Order` po
|
||||
where poi.prevdoc_docname = %s
|
||||
and poi.prevdoc_doctype = 'Sales Order'
|
||||
and poi.item_code = %s
|
||||
and poi.parent = po.name
|
||||
and po.status = 'Delivered'""", (self.name, item.item_code))
|
||||
|
||||
item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0
|
||||
item.db_set("delivered_qty", item_delivered_qty)
|
||||
|
||||
delivered_qty += item.delivered_qty
|
||||
tot_qty += item.qty
|
||||
|
||||
frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100)
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
list_context = get_list_context(context)
|
||||
@@ -268,12 +307,12 @@ def stop_or_unstop_sales_orders(names, status):
|
||||
for name in names:
|
||||
so = frappe.get_doc("Sales Order", name)
|
||||
if so.docstatus == 1:
|
||||
if status=="Stop":
|
||||
if so.status not in ("Stopped", "Cancelled") and (so.per_delivered < 100 or so.per_billed < 100):
|
||||
so.stop_sales_order()
|
||||
if status in ("Stopped", "Closed"):
|
||||
if so.status not in ("Stopped", "Cancelled", "Closed") and (so.per_delivered < 100 or so.per_billed < 100):
|
||||
so.update_status(status)
|
||||
else:
|
||||
if so.status == "Stopped":
|
||||
so.unstop_sales_order()
|
||||
if so.status in ("Stopped", "Closed"):
|
||||
so.update_status('Draft')
|
||||
|
||||
frappe.local.message_log = []
|
||||
|
||||
@@ -350,7 +389,7 @@ def make_delivery_note(source_name, target_doc=None):
|
||||
"parent": "against_sales_order",
|
||||
},
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.delivered_qty < doc.qty
|
||||
"condition": lambda doc: doc.delivered_qty < doc.qty and doc.delivered_by_supplier!=1
|
||||
},
|
||||
"Sales Taxes and Charges": {
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
@@ -488,3 +527,96 @@ def get_events(start, end, filters=None):
|
||||
"end": end
|
||||
}, as_dict=True, update={"allDay": 0})
|
||||
return data
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
target.supplier = for_supplier
|
||||
|
||||
default_price_list = frappe.get_value("Supplier", for_supplier, "default_price_list")
|
||||
if default_price_list:
|
||||
target.buying_price_list = default_price_list
|
||||
|
||||
target.delivered_by_supplier = 1
|
||||
target.run_method("set_missing_values")
|
||||
target.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(source, target, source_parent):
|
||||
target.schedule_date = source_parent.delivery_date
|
||||
target.qty = flt(source.qty) - flt(source.ordered_qty)
|
||||
|
||||
doclist = get_mapped_doc("Sales Order", source_name, {
|
||||
"Sales Order": {
|
||||
"doctype": "Purchase Order",
|
||||
"field_map": {
|
||||
"customer_address": "customer_address",
|
||||
"contact_person": "customer_contact_person",
|
||||
"address_display": "customer_address_display",
|
||||
"contact_display": "customer_contact_display",
|
||||
"contact_mobile": "customer_contact_mobile",
|
||||
"contact_email": "customer_contact_email",
|
||||
},
|
||||
"field_no_map": [
|
||||
"address_display",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"contact_person"
|
||||
],
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
}
|
||||
},
|
||||
"Sales Order Item": {
|
||||
"doctype": "Purchase Order Item",
|
||||
"field_map": [
|
||||
["name", "prevdoc_detail_docname"],
|
||||
["parent", "prevdoc_docname"],
|
||||
["parenttype", "prevdoc_doctype"],
|
||||
["uom", "stock_uom"],
|
||||
["delivery_date", "schedule_date"]
|
||||
],
|
||||
"field_no_map": [
|
||||
"rate",
|
||||
"price_list_rate"
|
||||
],
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == for_supplier
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_supplier(doctype, txt, searchfield, start, page_len, filters):
|
||||
supp_master_name = frappe.defaults.get_user_default("supp_master_name")
|
||||
if supp_master_name == "Supplier Name":
|
||||
fields = ["name", "supplier_type"]
|
||||
else:
|
||||
fields = ["name", "supplier_name", "supplier_type"]
|
||||
fields = ", ".join(fields)
|
||||
|
||||
return frappe.db.sql("""select {field} from `tabSupplier`
|
||||
where docstatus < 2
|
||||
and ({key} like %(txt)s
|
||||
or supplier_name like %(txt)s)
|
||||
and name in (select supplier from `tabSales Order Item` where parent = %(parent)s)
|
||||
order by
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999),
|
||||
name, supplier_name
|
||||
limit %(start)s, %(page_len)s """.format(**{
|
||||
'field': fields,
|
||||
'key': searchfield
|
||||
}), {
|
||||
'txt': "%%%s%%" % txt,
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start,
|
||||
'page_len': page_len,
|
||||
'parent': filters.get('parent')
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_status(status, name):
|
||||
so = frappe.get_doc("Sales Order", name)
|
||||
so.update_status(status)
|
||||
|
||||
@@ -5,6 +5,9 @@ frappe.listview_settings['Sales Order'] = {
|
||||
if(doc.status==="Stopped") {
|
||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
||||
|
||||
} else if(doc.status==="Closed"){
|
||||
return [__("Closed"), "green", "status,=,Closed"];
|
||||
|
||||
} else if (doc.order_type !== "Maintenance"
|
||||
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||
// to bill & overdue
|
||||
@@ -41,11 +44,15 @@ frappe.listview_settings['Sales Order'] = {
|
||||
onload: function(listview) {
|
||||
var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders";
|
||||
|
||||
listview.page.add_menu_item(__("Set as Stopped"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Stop"});
|
||||
listview.page.add_menu_item(__("Close"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Closed"});
|
||||
});
|
||||
|
||||
listview.page.add_menu_item(__("Set as Unstopped"), function() {
|
||||
listview.page.add_menu_item(__("Stop"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Stoped"});
|
||||
});
|
||||
|
||||
listview.page.add_menu_item(__("Re-open"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Unstop"});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ import frappe.permissions
|
||||
import unittest
|
||||
from erpnext.selling.doctype.sales_order.sales_order \
|
||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry \
|
||||
import make_journal_entry
|
||||
|
||||
from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||
|
||||
@@ -97,12 +95,12 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
# stop so
|
||||
so.load_from_db()
|
||||
so.stop_sales_order()
|
||||
so.update_status("Stopped")
|
||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
|
||||
|
||||
# unstop so
|
||||
so.load_from_db()
|
||||
so.unstop_sales_order()
|
||||
so.update_status('Draft')
|
||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
|
||||
|
||||
dn.cancel()
|
||||
@@ -147,14 +145,14 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
# stop so
|
||||
so.load_from_db()
|
||||
so.stop_sales_order()
|
||||
so.update_status("Stopped")
|
||||
|
||||
self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1)
|
||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2)
|
||||
|
||||
# unstop so
|
||||
so.load_from_db()
|
||||
so.unstop_sales_order()
|
||||
so.update_status('Draft')
|
||||
|
||||
self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 25)
|
||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
||||
@@ -296,6 +294,122 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
||||
|
||||
def test_drop_shipping(self):
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
|
||||
|
||||
po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "is_sales_item": 1,
|
||||
"is_purchase_item": 1, "delivered_by_supplier": 1, 'default_supplier': '_Test Supplier',
|
||||
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
})
|
||||
|
||||
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1, "is_sales_item": 1,
|
||||
"is_purchase_item": 1, "expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"})
|
||||
|
||||
so_items = [
|
||||
{
|
||||
"item_code": po_item.item_code,
|
||||
"warehouse": "",
|
||||
"qty": 2,
|
||||
"rate": 400,
|
||||
"delivered_by_supplier": 1,
|
||||
"supplier": '_Test Supplier'
|
||||
},
|
||||
{
|
||||
"item_code": dn_item.item_code,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 2,
|
||||
"rate": 300,
|
||||
"conversion_factor": 1.0
|
||||
}
|
||||
]
|
||||
|
||||
#setuo existing qty from bin
|
||||
bin = frappe.get_all("Bin", filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
|
||||
fields=["ordered_qty", "reserved_qty"])
|
||||
|
||||
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
|
||||
existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
|
||||
|
||||
bin = frappe.get_all("Bin", filters={"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"},
|
||||
fields=["reserved_qty"])
|
||||
|
||||
existing_reserved_qty_for_dn_item = bin[0].reserved_qty if bin else 0.0
|
||||
|
||||
#create so, po and partial dn
|
||||
so = make_sales_order(item_list=so_items, do_not_submit=True)
|
||||
so.submit()
|
||||
|
||||
po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier')
|
||||
po.submit()
|
||||
|
||||
dn = create_dn_against_so(so.name, delivered_qty=1)
|
||||
|
||||
self.assertEquals(so.customer, po.customer)
|
||||
self.assertEquals(po.items[0].prevdoc_doctype, "Sales Order")
|
||||
self.assertEquals(po.items[0].prevdoc_docname, so.name)
|
||||
self.assertEquals(po.items[0].item_code, po_item.item_code)
|
||||
self.assertEquals(dn.items[0].item_code, dn_item.item_code)
|
||||
|
||||
#test ordered_qty and reserved_qty
|
||||
ordered_qty, reserved_qty = frappe.db.get_value("Bin",
|
||||
{"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"])
|
||||
|
||||
self.assertEquals(abs(flt(ordered_qty)), existing_ordered_qty + so_items[0]['qty'])
|
||||
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
|
||||
|
||||
reserved_qty = frappe.db.get_value("Bin",
|
||||
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
|
||||
|
||||
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty_for_dn_item + 1)
|
||||
|
||||
#test po_item length
|
||||
self.assertEquals(len(po.items), 1)
|
||||
|
||||
#test per_delivered status
|
||||
update_status("Delivered", po.name)
|
||||
self.assertEquals(flt(frappe.db.get_value("Sales Order", so.name, "per_delivered"), 2), 75.00)
|
||||
|
||||
#test reserved qty after complete delivery
|
||||
dn = create_dn_against_so(so.name, delivered_qty=1)
|
||||
reserved_qty = frappe.db.get_value("Bin",
|
||||
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
|
||||
|
||||
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty_for_dn_item)
|
||||
|
||||
#test after closing so
|
||||
so.db_set('status', "Closed")
|
||||
so.update_reserved_qty()
|
||||
|
||||
ordered_qty, reserved_qty = frappe.db.get_value("Bin",
|
||||
{"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"])
|
||||
|
||||
self.assertEquals(abs(flt(ordered_qty)), existing_ordered_qty)
|
||||
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
|
||||
|
||||
reserved_qty = frappe.db.get_value("Bin",
|
||||
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
|
||||
|
||||
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
|
||||
|
||||
def test_reserved_qty_for_closing_so(self):
|
||||
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
||||
fields=["reserved_qty"])
|
||||
|
||||
existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
|
||||
|
||||
so = make_sales_order(item_code="_Test Item", qty=1)
|
||||
|
||||
self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty+1)
|
||||
|
||||
so.update_status("Closed")
|
||||
|
||||
self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty)
|
||||
|
||||
|
||||
def make_sales_order(**args):
|
||||
so = frappe.new_doc("Sales Order")
|
||||
args = frappe._dict(args)
|
||||
@@ -312,13 +426,18 @@ def make_sales_order(**args):
|
||||
if "warehouse" not in args:
|
||||
args.warehouse = "_Test Warehouse - _TC"
|
||||
|
||||
so.append("items", {
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
"warehouse": args.warehouse,
|
||||
"qty": args.qty or 10,
|
||||
"rate": args.rate or 100,
|
||||
"conversion_factor": 1.0,
|
||||
})
|
||||
if args.item_list:
|
||||
for item in args.item_list:
|
||||
so.append("items", item)
|
||||
|
||||
else:
|
||||
so.append("items", {
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
"warehouse": args.warehouse,
|
||||
"qty": args.qty or 10,
|
||||
"rate": args.rate or 100,
|
||||
"conversion_factor": 1.0,
|
||||
})
|
||||
|
||||
if not args.do_not_save:
|
||||
so.insert()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
|
||||
@@ -111,7 +111,8 @@ class EmailDigest(Document):
|
||||
"""Set standard digest style"""
|
||||
context.text_muted = '#8D99A6'
|
||||
context.text_color = '#36414C'
|
||||
context.h1 = 'margin-bottom: 30px; margin-bottom: 0; margin-top: 40px; font-weight: 400;'
|
||||
context.h1 = 'margin-bottom: 30px; margin-top: 40px; font-weight: 400; font-size: 30px;'
|
||||
context.h2 = 'margin-bottom: 30px; margin-top: -20px; font-weight: 400; font-size: 20px;'
|
||||
context.label_css = '''display: inline-block; color: {text_muted};
|
||||
padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
|
||||
context.section_head = 'margin-top: 60px; font-size: 16px;'
|
||||
@@ -139,7 +140,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)
|
||||
|
||||
@@ -152,7 +153,7 @@ class EmailDigest(Document):
|
||||
|
||||
todo_list = frappe.db.sql("""select *
|
||||
from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open"
|
||||
order by field(priority, 'High', 'Medium', 'Low') asc, date asc""",
|
||||
order by field(priority, 'High', 'Medium', 'Low') asc, date asc limit 20""",
|
||||
(user_id, user_id), as_dict=True)
|
||||
|
||||
for t in todo_list:
|
||||
@@ -168,7 +169,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:
|
||||
@@ -289,6 +290,7 @@ class EmailDigest(Document):
|
||||
elif self.frequency == "Weekly":
|
||||
# from date is the previous week's monday
|
||||
from_date = today - timedelta(days=today.weekday(), weeks=1)
|
||||
|
||||
# to date is sunday i.e. the previous day
|
||||
to_date = from_date + timedelta(days=6)
|
||||
else:
|
||||
@@ -300,32 +302,18 @@ class EmailDigest(Document):
|
||||
return from_date, to_date
|
||||
|
||||
def set_dates(self):
|
||||
today = now_datetime().date()
|
||||
self.future_from_date, self.future_to_date = self.from_date, self.to_date
|
||||
|
||||
# decide from date based on email digest frequency
|
||||
if self.frequency == "Daily":
|
||||
# from date, to_date is today
|
||||
self.future_from_date = self.future_to_date = today
|
||||
self.past_from_date = self.past_to_date = today - relativedelta(days = 1)
|
||||
self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days = 1)
|
||||
|
||||
elif self.frequency == "Weekly":
|
||||
# from date is the current week's monday
|
||||
self.future_from_date = today - relativedelta(days=today.weekday())
|
||||
|
||||
# to date is the current week's sunday
|
||||
self.future_to_date = self.future_from_date + relativedelta(days=6)
|
||||
|
||||
self.past_from_date = self.future_from_date - relativedelta(days=7)
|
||||
self.past_to_date = self.future_to_date - relativedelta(days=7)
|
||||
self.past_from_date = self.future_from_date - relativedelta(weeks=1)
|
||||
self.past_to_date = self.future_from_date - relativedelta(days=1)
|
||||
else:
|
||||
# from date is the 1st day of the current month
|
||||
self.future_from_date = today - relativedelta(days=today.day-1)
|
||||
|
||||
# to date is the last day of the current month
|
||||
self.future_to_date = self.future_from_date + relativedelta(days=-1, months=1)
|
||||
|
||||
self.past_from_date = self.future_from_date - relativedelta(month=1)
|
||||
self.past_to_date = self.future_to_date - relativedelta(month=1)
|
||||
self.past_from_date = self.future_from_date - relativedelta(months=1)
|
||||
self.past_to_date = self.future_from_date - relativedelta(days=1)
|
||||
|
||||
def get_next_sending(self):
|
||||
from_date, to_date = self.get_from_to_date()
|
||||
@@ -346,7 +334,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()
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<div style="max-width: 500px; margin: auto; padding: 20px 0 40px 0">
|
||||
|
||||
<h1 style="{{ h1 }}">{{ title }}</h1>
|
||||
<h2 style="{{ h2 }}">{{ company }}</h2>
|
||||
<h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;">
|
||||
<p>{% if frequency == "Daily" %}
|
||||
{{ frappe.format_date(future_from_date) }}
|
||||
@@ -52,6 +53,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 %}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import urllib
|
||||
from frappe.utils import nowdate
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
from frappe.website.render import clear_cache
|
||||
@@ -71,14 +72,16 @@ def get_product_list_for_group(product_group=None, start=0, limit=10):
|
||||
concat(parent_website_route, "/", page_name) as route
|
||||
from `tabItem`
|
||||
where show_in_website = 1
|
||||
and disabled=0
|
||||
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
|
||||
and (variant_of = '' or variant_of is null)
|
||||
and (item_group in (%s)
|
||||
or name in (select parent from `tabWebsite Item Group` where item_group in (%s)))
|
||||
""" % (child_groups, child_groups)
|
||||
and (item_group in ({child_groups})
|
||||
or name in (select parent from `tabWebsite Item Group` where item_group in ({child_groups})))
|
||||
""".format(child_groups=child_groups)
|
||||
|
||||
query += """order by weightage desc, modified desc limit %s, %s""" % (start, limit)
|
||||
|
||||
data = frappe.db.sql(query, {"product_group": product_group}, as_dict=1)
|
||||
data = frappe.db.sql(query, {"product_group": product_group, "today": nowdate()}, as_dict=1)
|
||||
|
||||
return [get_item_for_list_in_html(r) for r in data]
|
||||
|
||||
|
||||
@@ -658,7 +658,7 @@ $.extend(erpnext.wiz, {
|
||||
return frappe.render_template("setup_wizard_message", {
|
||||
image: "/assets/frappe/images/ui/bubble-tea-happy.svg",
|
||||
title: __('Setup Complete'),
|
||||
message: __('Your setup is complete. Refreshing.') + ".."
|
||||
message: ""
|
||||
});
|
||||
},
|
||||
|
||||
@@ -670,6 +670,7 @@ $.extend(erpnext.wiz, {
|
||||
args: values,
|
||||
callback: function(r) {
|
||||
wiz.show_complete();
|
||||
localStorage.setItem("session_last_route", "#welcome-to-erpnext");
|
||||
setTimeout(function() {
|
||||
window.location = "/desk";
|
||||
}, 2000);
|
||||
|
||||
@@ -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
|
||||
|
||||
0
erpnext/setup/page/welcome_to_erpnext/__init__.py
Normal file
0
erpnext/setup/page/welcome_to_erpnext/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user