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.
|
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)
|
- [User Guide](https://manual.erpnext.com)
|
||||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
from __future__ import unicode_literals
|
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)
|
self.validate_warehouse(old_warehouse)
|
||||||
if self.warehouse:
|
if self.warehouse:
|
||||||
self.validate_warehouse(self.warehouse)
|
self.validate_warehouse(self.warehouse)
|
||||||
|
elif self.warehouse:
|
||||||
|
self.warehouse = None
|
||||||
|
|
||||||
def validate_warehouse(self, warehouse):
|
def validate_warehouse(self, warehouse):
|
||||||
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}):
|
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
from frappe.utils import flt, fmt_money, getdate, formatdate
|
||||||
from frappe.model.document import Document
|
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.accounts.utils import get_account_currency
|
||||||
from erpnext.setup.doctype.company.company import get_company_currency
|
from erpnext.setup.doctype.company.company import get_company_currency
|
||||||
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
|
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
|
||||||
@@ -114,13 +114,7 @@ class GLEntry(Document):
|
|||||||
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
|
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
|
||||||
|
|
||||||
if self.party_type and self.party:
|
if self.party_type and self.party:
|
||||||
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
|
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def validate_balance_type(account, adv_adj=False):
|
def validate_balance_type(account, adv_adj=False):
|
||||||
if not adv_adj and account:
|
if not adv_adj and account:
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ frappe.require("assets/erpnext/js/utils.js");
|
|||||||
frappe.ui.form.on("Journal Entry", {
|
frappe.ui.form.on("Journal Entry", {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
cur_frm.cscript.voucher_type(frm.doc);
|
frm.cscript.voucher_type(frm.doc);
|
||||||
|
|
||||||
if(frm.doc.docstatus==1) {
|
if(frm.doc.docstatus==1) {
|
||||||
cur_frm.add_custom_button(__('View Ledger'), function() {
|
frm.add_custom_button(__('View Ledger'), function() {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
"voucher_no": frm.doc.name,
|
"voucher_no": frm.doc.name,
|
||||||
"from_date": frm.doc.posting_date,
|
"from_date": frm.doc.posting_date,
|
||||||
@@ -23,6 +23,12 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
}, "icon-table");
|
}, "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
|
// hide /unhide fields based on currency
|
||||||
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
|
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
|
||||||
},
|
},
|
||||||
@@ -32,24 +38,6 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
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({
|
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||||
onload: function() {
|
onload: function() {
|
||||||
this.load_defaults();
|
this.load_defaults();
|
||||||
@@ -69,7 +57,7 @@ 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();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -77,16 +65,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
|
me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
|
||||||
var filters = {
|
return erpnext.journal_entry.account_query(me.frm);
|
||||||
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 };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
|
me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
|
||||||
@@ -244,6 +223,7 @@ cur_frm.cscript.company = function(doc, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.posting_date = 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);
|
erpnext.get_fiscal_year(doc.company, doc.posting_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,18 +379,40 @@ frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) {
|
|||||||
cur_frm.cscript.update_totals(frm.doc);
|
cur_frm.cscript.update_totals(frm.doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
erpnext.journal_entry.set_debit_credit_in_company_currency = function(frm, cdt, cdn) {
|
$.extend(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;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
set_debit_credit_in_company_currency: function(frm, cdt, cdn) {
|
||||||
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
|
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
|
||||||
|
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
|
|
||||||
frappe.model.set_value(cdt, cdn, "debit",
|
frappe.model.set_value(cdt, cdn, "debit",
|
||||||
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
|
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
|
||||||
|
|
||||||
frappe.model.set_value(cdt, cdn, "credit",
|
frappe.model.set_value(cdt, cdn, "credit",
|
||||||
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
|
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
|
||||||
}
|
|
||||||
|
|
||||||
erpnext.journal_entry.set_exchange_rate = function(frm, cdt, cdn) {
|
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 company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
|
|
||||||
@@ -436,4 +438,75 @@ erpnext.journal_entry.set_exchange_rate = function(frm, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
|
from erpnext.accounts.party import get_party_account
|
||||||
|
|
||||||
class JournalEntry(AccountsController):
|
class JournalEntry(AccountsController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, arg1, arg2=None):
|
||||||
@@ -27,6 +27,7 @@ class JournalEntry(AccountsController):
|
|||||||
self.validate_cheque_info()
|
self.validate_cheque_info()
|
||||||
self.validate_entries_for_advance()
|
self.validate_entries_for_advance()
|
||||||
self.validate_multi_currency()
|
self.validate_multi_currency()
|
||||||
|
self.set_amounts_in_company_currency()
|
||||||
self.validate_debit_and_credit()
|
self.validate_debit_and_credit()
|
||||||
self.validate_against_jv()
|
self.validate_against_jv()
|
||||||
self.validate_reference_doc()
|
self.validate_reference_doc()
|
||||||
@@ -175,6 +176,9 @@ class JournalEntry(AccountsController):
|
|||||||
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
||||||
[scrub(dt) for dt in field_dict.get(d.reference_type)])
|
[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
|
# check if party and account match
|
||||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||||
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
|
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
|
||||||
@@ -280,6 +284,7 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
self.set_exchange_rate()
|
self.set_exchange_rate()
|
||||||
|
|
||||||
|
def set_amounts_in_company_currency(self):
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
|
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"))
|
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
|
||||||
@@ -498,14 +503,17 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
|||||||
if voucher_type=="Bank Entry":
|
if voucher_type=="Bank Entry":
|
||||||
account = frappe.db.get_value("Company", company, "default_bank_account")
|
account = frappe.db.get_value("Company", company, "default_bank_account")
|
||||||
if not 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":
|
elif voucher_type=="Cash Entry":
|
||||||
account = frappe.db.get_value("Company", company, "default_cash_account")
|
account = frappe.db.get_value("Company", company, "default_cash_account")
|
||||||
if not 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:
|
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 {
|
return {
|
||||||
"account": account,
|
"account": account,
|
||||||
"balance": get_balance_on(account),
|
"balance": get_balance_on(account),
|
||||||
@@ -514,206 +522,116 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry_from_sales_invoice(sales_invoice):
|
def get_payment_entry_against_order(dt, dn):
|
||||||
"""Returns new Journal Entry document as dict for given Sales Invoice"""
|
ref_doc = frappe.get_doc(dt, dn)
|
||||||
from erpnext.accounts.utils import get_balance_on
|
|
||||||
si = frappe.get_doc("Sales Invoice", sales_invoice)
|
|
||||||
|
|
||||||
# exchange rate
|
if flt(ref_doc.per_billed, 2) > 0:
|
||||||
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company,
|
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||||
si.doctype, si.name)
|
|
||||||
|
|
||||||
jv = get_payment_entry(si)
|
if dt == "Sales Order":
|
||||||
jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
|
party_type = "Customer"
|
||||||
|
amount_field_party = "credit_in_account_currency"
|
||||||
# credit customer
|
amount_field_bank = "debit_in_account_currency"
|
||||||
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
|
|
||||||
else:
|
else:
|
||||||
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate
|
party_type = "Supplier"
|
||||||
|
amount_field_party = "debit_in_account_currency"
|
||||||
|
amount_field_bank = "credit_in_account_currency"
|
||||||
|
|
||||||
# set multi currency check
|
party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
|
||||||
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_account_currency = get_account_currency(party_account)
|
party_account_currency = get_account_currency(party_account)
|
||||||
|
|
||||||
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
|
if party_account_currency == ref_doc.company_currency:
|
||||||
|
amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid)
|
||||||
if party_account_currency == so.company_currency:
|
|
||||||
amount = flt(so.base_grand_total) - flt(so.advance_paid)
|
|
||||||
else:
|
else:
|
||||||
amount = flt(so.grand_total) - flt(so.advance_paid)
|
amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
||||||
|
|
||||||
# credit customer
|
return get_payment_entry(ref_doc, {
|
||||||
row1 = jv.get("accounts")[0]
|
"party_type": party_type,
|
||||||
row1.account = party_account
|
"party_account": party_account,
|
||||||
row1.account_currency = party_account_currency
|
"party_account_currency": party_account_currency,
|
||||||
row1.party_type = "Customer"
|
"amount_field_party": amount_field_party,
|
||||||
row1.party = so.customer
|
"amount_field_bank": amount_field_bank,
|
||||||
row1.balance = get_balance_on(party_account)
|
"amount": amount,
|
||||||
row1.party_balance = get_balance_on(party=so.customer, party_type="Customer")
|
"remarks": 'Advance Payment received against {0} {1}'.format(dt, dn),
|
||||||
row1.credit_in_account_currency = amount
|
"is_advance": "Yes"
|
||||||
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()
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry_from_purchase_order(purchase_order):
|
def get_payment_entry_against_invoice(dt, dn):
|
||||||
"""Returns new Journal Entry document as dict for given Sales Order"""
|
ref_doc = frappe.get_doc(dt, dn)
|
||||||
from erpnext.accounts.utils import get_balance_on
|
if dt == "Sales Invoice":
|
||||||
from erpnext.accounts.party import get_party_account
|
party_type = "Customer"
|
||||||
po = frappe.get_doc("Purchase Order", purchase_order)
|
party_account = ref_doc.debit_to
|
||||||
|
amount_field_party = "credit_in_account_currency"
|
||||||
if flt(po.per_billed, 2) != 0.0:
|
amount_field_bank = "debit_in_account_currency"
|
||||||
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)
|
|
||||||
else:
|
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"
|
||||||
|
|
||||||
# credit customer
|
return get_payment_entry(ref_doc, {
|
||||||
row1 = jv.get("accounts")[0]
|
"party_type": party_type,
|
||||||
row1.account = party_account
|
"party_account": party_account,
|
||||||
row1.party_type = "Supplier"
|
"party_account_currency": ref_doc.party_account_currency,
|
||||||
row1.party = po.supplier
|
"amount_field_party": amount_field_party,
|
||||||
row1.balance = get_balance_on(party_account)
|
"amount_field_bank": amount_field_bank,
|
||||||
row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
|
"amount": ref_doc.outstanding_amount,
|
||||||
row1.debit_in_account_currency = amount
|
"remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks),
|
||||||
row1.reference_type = po.doctype
|
"is_advance": "No"
|
||||||
row1.reference_name = po.name
|
})
|
||||||
row1.is_advance = "Yes"
|
|
||||||
row1.exchange_rate = exchange_rate
|
|
||||||
row1.account_type = "Payable"
|
|
||||||
|
|
||||||
# debit bank
|
def get_payment_entry(ref_doc, args):
|
||||||
row2 = jv.get("accounts")[1]
|
cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center")
|
||||||
if row2.account_currency == party_account_currency:
|
exchange_rate = get_exchange_rate(args.get("party_account"), args.get("party_account_currency"),
|
||||||
row2.credit_in_account_currency = amount
|
ref_doc.company, ref_doc.doctype, ref_doc.name)
|
||||||
else:
|
|
||||||
row2.credit_in_account_currency = amount * exchange_rate
|
|
||||||
|
|
||||||
# set multi currency check
|
jv = frappe.new_doc("Journal Entry")
|
||||||
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency:
|
jv.update({
|
||||||
jv.multi_currency = 1
|
"voucher_type": "Bank Entry",
|
||||||
|
"company": ref_doc.company,
|
||||||
|
"remark": args.get("remarks")
|
||||||
|
})
|
||||||
|
|
||||||
return jv.as_dict()
|
party_row = jv.append("accounts", {
|
||||||
|
"account": args.get("party_account"),
|
||||||
def get_payment_entry(doc):
|
"party_type": args.get("party_type"),
|
||||||
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
|
"party": ref_doc.get(args.get("party_type").lower()),
|
||||||
|
"cost_center": cost_center,
|
||||||
jv = frappe.new_doc('Journal Entry')
|
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
|
||||||
jv.voucher_type = 'Bank Entry'
|
"account_currency": args.get("party_account_currency") or \
|
||||||
jv.company = doc.company
|
get_account_currency(args.get("party_account")),
|
||||||
jv.fiscal_year = doc.fiscal_year
|
"account_balance": get_balance_on(args.get("party_account")),
|
||||||
|
"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
|
||||||
jv.append("accounts")
|
"exchange_rate": exchange_rate,
|
||||||
d2 = jv.append("accounts")
|
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:
|
if bank_account:
|
||||||
d2.account = bank_account["account"]
|
bank_row.update(bank_account)
|
||||||
d2.balance = bank_account["balance"]
|
bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
|
||||||
d2.account_currency = bank_account["account_currency"]
|
bank_account["account_currency"], ref_doc.company)
|
||||||
d2.account_type = bank_account["account_type"]
|
|
||||||
d2.exchange_rate = get_exchange_rate(bank_account["account"],
|
|
||||||
bank_account["account_currency"], doc.company)
|
|
||||||
|
|
||||||
return jv
|
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)
|
||||||
|
|
||||||
|
# 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()
|
@frappe.whitelist()
|
||||||
def get_opening_accounts(company):
|
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"):
|
if not frappe.has_permission("Account"):
|
||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
from erpnext.accounts.party import get_party_account
|
|
||||||
account = get_party_account(party_type, party, company)
|
account = get_party_account(party_type, party, company)
|
||||||
|
|
||||||
account_balance = get_balance_on(account=account)
|
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
|
return grid_values
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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):
|
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
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)
|
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 account_currency != company_currency:
|
||||||
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
|
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{
|
return{
|
||||||
filters:{
|
filters:{
|
||||||
"company": doc.company,
|
"company": doc.company,
|
||||||
"report_type": "Balance Sheet",
|
"root_type": "Liability",
|
||||||
"freeze_account": "No",
|
"freeze_account": "No",
|
||||||
"is_group": 0
|
"is_group": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,29 +194,6 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -250,7 +227,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-02 07:39:00.056337",
|
"modified": "2015-10-21 12:40:58.278256",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Period Closing Voucher",
|
"name": "Period Closing Voucher",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
class PeriodClosingVoucher(AccountsController):
|
class PeriodClosingVoucher(AccountsController):
|
||||||
@@ -20,51 +21,75 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
|
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
|
||||||
|
|
||||||
def validate_account_head(self):
|
def validate_account_head(self):
|
||||||
if frappe.db.get_value("Account", self.closing_account_head, "report_type") \
|
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
|
||||||
!= "Balance Sheet":
|
|
||||||
frappe.throw(_("Closing Account {0} must be of type 'Liability'").format(self.closing_account_head))
|
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):
|
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]
|
self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year)[1]
|
||||||
|
|
||||||
pce = frappe.db.sql("""select name from `tabPeriod Closing Voucher`
|
pce = frappe.db.sql("""select name from `tabPeriod Closing Voucher`
|
||||||
where posting_date > %s and fiscal_year = %s and docstatus = 1""",
|
where posting_date > %s and fiscal_year = %s and docstatus = 1""",
|
||||||
(self.posting_date, self.fiscal_year))
|
(self.posting_date, self.fiscal_year))
|
||||||
if pce and pce[0][0]:
|
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))
|
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_in_company_currency):
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": acc.account,
|
||||||
|
"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_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):
|
def get_pl_balances(self):
|
||||||
"""Get balance for pl accounts"""
|
"""Get balance for pl accounts"""
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select t1.account, sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
|
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
|
from `tabGL Entry` t1, `tabAccount` t2
|
||||||
where t1.account = t2.name and ifnull(t2.report_type, '') = 'Profit and Loss'
|
where t1.account = t2.name and ifnull(t2.report_type, '') = 'Profit and Loss'
|
||||||
and t2.docstatus < 2 and t2.company = %s
|
and t2.docstatus < 2 and t2.company = %s
|
||||||
and t1.posting_date between %s and %s
|
and t1.posting_date between %s and %s
|
||||||
group by t1.account
|
group by t1.account
|
||||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||||
|
|
||||||
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):
|
|
||||||
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,
|
|
||||||
}))
|
|
||||||
|
|
||||||
net_pl_balance += flt(acc.balance)
|
|
||||||
|
|
||||||
if net_pl_balance:
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
|
||||||
"account": self.closing_account_head,
|
|
||||||
"debit": 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)
|
|
||||||
|
|||||||
@@ -5,42 +5,74 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest
|
||||||
import frappe
|
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
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
|
|
||||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||||
def test_closing_entry(self):
|
def test_closing_entry(self):
|
||||||
|
year_start_date = get_fiscal_year(today())[1]
|
||||||
|
|
||||||
make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
|
make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
|
||||||
"_Test Cost Center - _TC", submit=True)
|
"_Test Cost Center - _TC", submit=True)
|
||||||
|
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC", 600, "_Test Cost Center - _TC", submit=True)
|
"_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
|
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
|
from `tabGL Entry` t1, `tabAccount` t2
|
||||||
where t1.account = t2.name and ifnull(t2.report_type, '') = 'Profit and Loss'
|
where t1.account = t2.name and ifnull(t2.report_type, '') = 'Profit and Loss'
|
||||||
and t2.docstatus < 2 and t2.company = '_Test Company'
|
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
|
profit_or_loss = flt(profit_or_loss[0][0]) if profit_or_loss else 0
|
||||||
|
|
||||||
pcv = self.make_period_closing_voucher()
|
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
|
from `tabGL Entry` where voucher_type='Period Closing Voucher' and voucher_no=%s
|
||||||
and account = '_Test Account Reserves and Surplus - _TC'""", pcv.name)
|
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):
|
def make_period_closing_voucher(self):
|
||||||
pcv = frappe.get_doc({
|
pcv = frappe.get_doc({
|
||||||
"doctype": "Period Closing Voucher",
|
"doctype": "Period Closing Voucher",
|
||||||
"closing_account_head": "_Test Account Reserves and Surplus - _TC",
|
"closing_account_head": "_Test Account Reserves and Surplus - _TC",
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"fiscal_year": get_fiscal_year(today())[0],
|
||||||
"posting_date": "2013-12-31",
|
"posting_date": today(),
|
||||||
"remarks": "test"
|
"remarks": "test"
|
||||||
})
|
})
|
||||||
pcv.insert()
|
pcv.insert()
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
supplier: cur_frm.doc.supplier || undefined,
|
supplier: cur_frm.doc.supplier || undefined,
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
status: ["!=", "Stopped"],
|
status: ["not in", ["Stopped", "Closed"]],
|
||||||
per_billed: ["<", 99.99],
|
per_billed: ["<", 99.99],
|
||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
@@ -52,6 +52,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
supplier: cur_frm.doc.supplier || undefined,
|
supplier: cur_frm.doc.supplier || undefined,
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
|
status: ["!=", "Closed"],
|
||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -135,9 +136,10 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
|||||||
|
|
||||||
cur_frm.cscript.make_bank_entry = function() {
|
cur_frm.cscript.make_bank_entry = function() {
|
||||||
return frappe.call({
|
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: {
|
args: {
|
||||||
"purchase_invoice": cur_frm.doc.name,
|
"dt": "Purchase Invoice",
|
||||||
|
"dn": cur_frm.doc.name
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.check_conversion_rate()
|
self.check_conversion_rate()
|
||||||
self.validate_credit_to_acc()
|
self.validate_credit_to_acc()
|
||||||
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
|
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_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
self.set_against_expense_account()
|
self.set_against_expense_account()
|
||||||
@@ -103,14 +103,14 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.party_account_currency = account.account_currency
|
self.party_account_currency = account.account_currency
|
||||||
|
|
||||||
def check_for_stopped_status(self):
|
def check_for_stopped_or_closed_status(self):
|
||||||
check_list = []
|
check_list = []
|
||||||
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
|
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
|
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
|
||||||
check_list.append(d.purchase_order)
|
check_list.append(d.purchase_order)
|
||||||
stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order)
|
pc_obj.check_for_stopped_or_closed_status('Purchase Order', d.purchase_order)
|
||||||
if stopped:
|
|
||||||
throw(_("Purchase Order {0} is 'Stopped'").format(d.purchase_order))
|
|
||||||
|
|
||||||
def validate_with_previous_doc(self):
|
def validate_with_previous_doc(self):
|
||||||
super(PurchaseInvoice, self).validate_with_previous_doc({
|
super(PurchaseInvoice, self).validate_with_previous_doc({
|
||||||
@@ -317,13 +317,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if auto_accounting_for_stock and self.is_opening == "No" and \
|
if auto_accounting_for_stock and self.is_opening == "No" and \
|
||||||
item.item_code in stock_items and item.item_tax_amount:
|
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
|
# 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:
|
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""",
|
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
||||||
(item.purchase_receipt, expenses_included_in_valuation))
|
(item.purchase_receipt, expenses_included_in_valuation))
|
||||||
|
|
||||||
if not negative_expense_booked_in_pi:
|
if not negative_expense_booked_in_pr:
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": stock_received_but_not_billed,
|
"account": stock_received_but_not_billed,
|
||||||
@@ -395,6 +394,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
self.check_for_stopped_or_closed_status()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||||
remove_against_link_from_jv(self.doctype, self.name)
|
remove_against_link_from_jv(self.doctype, self.name)
|
||||||
|
|||||||
@@ -50,23 +50,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
self.check_gle_for_pi(pi.name)
|
||||||
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)
|
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
@@ -83,9 +67,14 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
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
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
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)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
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][1], gle.debit)
|
||||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
|
||||||
|
|
||||||
def test_gl_entries_with_aia_for_non_stock_items(self):
|
def test_gl_entries_with_aia_for_non_stock_items(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
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.update_stock) this.show_stock_ledger();
|
||||||
|
|
||||||
if(doc.docstatus==1 && !doc.is_return) {
|
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'),
|
cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'),
|
||||||
this.make_sales_return);
|
this.make_sales_return);
|
||||||
|
|
||||||
@@ -61,7 +68,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
return item.delivery_note ? true : false;
|
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");
|
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",
|
source_doctype: "Sales Order",
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
status: ["!=", "Stopped"],
|
status: ["not in", ["Stopped", "Closed"]],
|
||||||
per_billed: ["<", 99.99],
|
per_billed: ["<", 99.99],
|
||||||
customer: cur_frm.doc.customer || undefined,
|
customer: cur_frm.doc.customer || undefined,
|
||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
@@ -160,6 +167,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
me.set_dynamic_labels();
|
me.set_dynamic_labels();
|
||||||
me.calculate_taxes_and_totals();
|
me.calculate_taxes_and_totals();
|
||||||
if(callback_fn) callback_fn();
|
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() {
|
cur_frm.cscript.make_bank_entry = function() {
|
||||||
return frappe.call({
|
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: {
|
args: {
|
||||||
"sales_invoice": cur_frm.doc.name
|
"dt": "Sales Invoice",
|
||||||
|
"dn": cur_frm.doc.name
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
|||||||
@@ -2951,7 +2951,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-02 07:39:09.123982",
|
"modified": "2015-10-26 12:12:40.616546",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.validate_proj_cust()
|
self.validate_proj_cust()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("stock_uom", "qty")
|
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_debit_to_acc()
|
||||||
self.validate_fixed_asset_account()
|
self.validate_fixed_asset_account()
|
||||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||||
@@ -117,7 +117,7 @@ class SalesInvoice(SellingController):
|
|||||||
if cint(self.update_stock) == 1:
|
if cint(self.update_stock) == 1:
|
||||||
self.update_stock_ledger()
|
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
|
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||||
remove_against_link_from_jv(self.doctype, self.name)
|
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",
|
"sales_order": "against_sales_order",
|
||||||
"so_detail": "so_detail"
|
"so_detail": "so_detail"
|
||||||
},
|
},
|
||||||
"postprocess": update_item
|
"postprocess": update_item,
|
||||||
|
"condition": lambda doc: doc.delivered_by_supplier!=1
|
||||||
},
|
},
|
||||||
"Sales Taxes and Charges": {
|
"Sales Taxes and Charges": {
|
||||||
"doctype": "Sales Taxes and Charges",
|
"doctype": "Sales Taxes and Charges",
|
||||||
|
|||||||
@@ -680,6 +680,51 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -1282,7 +1327,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-19 03:04:52.093181",
|
"modified": "2015-11-02 15:14:02.306067",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ def get_party_details(party, party_type, args=None):
|
|||||||
def get_tax_template(posting_date, args):
|
def get_tax_template(posting_date, args):
|
||||||
"""Get matching tax rule"""
|
"""Get matching tax rule"""
|
||||||
args = frappe._dict(args)
|
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():
|
for key, value in args.iteritems():
|
||||||
if key in "use_for_shopping_cart":
|
if key in "use_for_shopping_cart":
|
||||||
@@ -117,16 +118,16 @@ def get_tax_template(posting_date, args):
|
|||||||
else:
|
else:
|
||||||
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
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)
|
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
||||||
|
|
||||||
if not matching:
|
if not tax_rule:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for rule in matching:
|
for rule in tax_rule:
|
||||||
rule.no_of_keys_matched = 0
|
rule.no_of_keys_matched = 0
|
||||||
for key in args:
|
for key in args:
|
||||||
if rule.get(key): rule.no_of_keys_matched += 1
|
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
|
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'),
|
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
||||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
||||||
options: ['', 'Bank', 'Cash', 'Warehouse', 'Receivable', 'Payable',
|
options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'),
|
||||||
'Equity', 'Cost of Goods Sold', 'Fixed Asset', 'Expense Account',
|
|
||||||
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
|
|
||||||
description: __("Optional. This setting will be used to filter in various transactions.") },
|
description: __("Optional. This setting will be used to filter in various transactions.") },
|
||||||
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
|
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
|
||||||
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
|
{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 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"""
|
"""Validate party account currency with existing GL Entry's currency"""
|
||||||
|
if not party_account_currency:
|
||||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||||
|
|
||||||
existing_gle_currency = get_party_gle_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:
|
if existing_gle_currency and party_account_currency != existing_gle_currency:
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"creation": "2013-07-30 17:28:49",
|
"creation": "2013-07-30 17:28:49",
|
||||||
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2015-03-30 05:33:45.353064",
|
"modified": "2015-11-02 12:32:02.048551",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Delivered Items To Be Billed",
|
"name": "Delivered Items To Be Billed",
|
||||||
"owner": "Administrator",
|
"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",
|
"ref_doctype": "Sales Invoice",
|
||||||
"report_name": "Delivered Items To Be Billed",
|
"report_name": "Delivered Items To Be Billed",
|
||||||
"report_type": "Query Report"
|
"report_type": "Query Report"
|
||||||
|
|||||||
@@ -98,11 +98,17 @@ class GrossProfitGenerator(object):
|
|||||||
|
|
||||||
row.base_amount = flt(row.base_net_amount)
|
row.base_amount = flt(row.base_net_amount)
|
||||||
|
|
||||||
|
if row.update_stock:
|
||||||
product_bundles = self.product_bundles.get(row.parenttype, {}).get(row.parent, frappe._dict())
|
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
|
# get buying amount
|
||||||
if row.item_code in product_bundles:
|
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:
|
else:
|
||||||
row.buying_amount = self.get_buying_amount(row, row.item_code)
|
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.qty += row.qty
|
||||||
new_row.buying_amount += row.buying_amount
|
new_row.buying_amount += row.buying_amount
|
||||||
new_row.base_amount += row.base_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 = new_row.base_amount - new_row.buying_amount
|
||||||
new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \
|
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):
|
def get_buying_amount_from_product_bundle(self, row, product_bundle):
|
||||||
buying_amount = 0.0
|
buying_amount = 0.0
|
||||||
for bom_item in product_bundle:
|
for packed_item in product_bundle:
|
||||||
if bom_item.get("parent_detail_docname")==row.item_row:
|
if packed_item.get("parent_detail_docname")==row.item_row:
|
||||||
buying_amount += self.get_buying_amount(row, bom_item.item_code)
|
buying_amount += self.get_buying_amount(row, packed_item.item_code)
|
||||||
|
|
||||||
return buying_amount
|
return buying_amount
|
||||||
|
|
||||||
@@ -176,14 +181,14 @@ class GrossProfitGenerator(object):
|
|||||||
else:
|
else:
|
||||||
my_sle = self.sle.get((item_code, row.warehouse))
|
my_sle = self.sle.get((item_code, row.warehouse))
|
||||||
if (row.update_stock or row.dn_detail) and my_sle:
|
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:
|
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):
|
for i, sle in enumerate(my_sle):
|
||||||
# find the stock valution rate from stock ledger entry
|
# find the stock valution rate from stock ledger entry
|
||||||
if sle.voucher_type == parenttype and parent == sle.voucher_no and \
|
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 \
|
previous_stock_value = len(my_sle) > i+1 and \
|
||||||
flt(my_sle[i+1].stock_value) or 0.0
|
flt(my_sle[i+1].stock_value) or 0.0
|
||||||
return previous_stock_value - flt(sle.stock_value)
|
return previous_stock_value - flt(sle.stock_value)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"creation": "2013-02-21 14:26:44",
|
"creation": "2013-02-21 14:26:44",
|
||||||
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2015-03-30 05:33:29.382709",
|
"modified": "2015-11-04 11:56:32.699103",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Ordered Items To Be Billed",
|
"name": "Ordered Items To Be Billed",
|
||||||
"owner": "Administrator",
|
"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",
|
"ref_doctype": "Sales Invoice",
|
||||||
"report_name": "Ordered Items To Be Billed",
|
"report_name": "Ordered Items To Be Billed",
|
||||||
"report_type": "Query Report"
|
"report_type": "Query Report"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
|
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):
|
def execute(filters=None):
|
||||||
if not filters: filters = {}
|
if not filters: filters = {}
|
||||||
@@ -13,24 +13,26 @@ def execute(filters=None):
|
|||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
entries = get_entries(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 = ""
|
against_date = ""
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for d in entries:
|
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":
|
if d.reference_type=="Purchase Invoice":
|
||||||
payment_amount = flt(d.debit) or -1 * flt(d.credit)
|
payment_amount = flt(d.debit) or -1 * flt(d.credit)
|
||||||
else:
|
else:
|
||||||
payment_amount = flt(d.credit) or -1 * flt(d.debit)
|
payment_amount = flt(d.credit) or -1 * flt(d.debit)
|
||||||
|
|
||||||
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name,
|
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
|
||||||
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
||||||
|
|
||||||
if d.reference_name:
|
if d.reference_name:
|
||||||
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
|
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
|
||||||
else:
|
else:
|
||||||
row += ["", "", "", "", ""]
|
row += ["", "", "", "", ""]
|
||||||
|
if invoice.due_date:
|
||||||
|
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
@@ -43,13 +45,25 @@ def validate_filters(filters):
|
|||||||
.format(filters.payment_type, filters.party_type))
|
.format(filters.payment_type, filters.party_type))
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
return [_("Journal Entry") + ":Link/Journal Entry:140",
|
return [
|
||||||
_("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140",
|
_("Journal Entry") + ":Link/Journal Entry:140",
|
||||||
|
_("Party Type") + "::100",
|
||||||
|
_("Party") + ":Dynamic Link/Party Type:140",
|
||||||
_("Posting Date") + ":Date:100",
|
_("Posting Date") + ":Date:100",
|
||||||
_("Against Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
_("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",
|
_("Invoice Posting Date") + ":Date:130",
|
||||||
_("Reference No") + "::100", _("Reference Date") + ":Date:100", _("Remarks") + "::150", _("Age") +":Int:40",
|
_("Payment Due Date") + ":Date:130",
|
||||||
"0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100"
|
_("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):
|
def get_conditions(filters):
|
||||||
@@ -67,6 +81,13 @@ def get_conditions(filters):
|
|||||||
if filters.get("party"):
|
if filters.get("party"):
|
||||||
conditions.append("jvd.party=%(party)s")
|
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"):
|
if filters.get("company"):
|
||||||
conditions.append("jv.company=%(company)s")
|
conditions.append("jv.company=%(company)s")
|
||||||
|
|
||||||
@@ -89,12 +110,9 @@ def get_entries(filters):
|
|||||||
return entries
|
return entries
|
||||||
|
|
||||||
def get_invoice_posting_date_map(filters):
|
def get_invoice_posting_date_map(filters):
|
||||||
invoice_posting_date_map = {}
|
invoice_details = {}
|
||||||
if filters.get("payment_type") == "Incoming":
|
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
|
||||||
for t in frappe.db.sql("""select name, posting_date from `tabSales Invoice`"""):
|
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
|
||||||
invoice_posting_date_map[t[0]] = t[1]
|
invoice_details[t.name] = t
|
||||||
else:
|
|
||||||
for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""):
|
|
||||||
invoice_posting_date_map[t[0]] = t[1]
|
|
||||||
|
|
||||||
return invoice_posting_date_map
|
return invoice_details
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
"add_total_row": 1,
|
"add_total_row": 1,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"creation": "2013-05-28 15:54:16",
|
"creation": "2013-05-28 15:54:16",
|
||||||
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2015-03-30 05:37:23.626083",
|
"modified": "2015-11-04 11:56:14.321664",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Order Items To Be Billed",
|
"name": "Purchase Order Items To Be Billed",
|
||||||
"owner": "Administrator",
|
"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",
|
"ref_doctype": "Purchase Invoice",
|
||||||
"report_name": "Purchase Order Items To Be Billed",
|
"report_name": "Purchase Order Items To Be Billed",
|
||||||
"report_type": "Query Report"
|
"report_type": "Query Report"
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"creation": "2013-07-30 18:35:10",
|
"creation": "2013-07-30 18:35:10",
|
||||||
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2015-04-14 11:56:02.323769",
|
"modified": "2015-11-02 12:33:11.681513",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Received Items To Be Billed",
|
"name": "Received Items To Be Billed",
|
||||||
"owner": "Administrator",
|
"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",
|
"ref_doctype": "Purchase Invoice",
|
||||||
"report_name": "Received Items To Be Billed",
|
"report_name": "Received Items To Be Billed",
|
||||||
"report_type": "Query Report"
|
"report_type": "Query Report"
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class PurchaseCommon(BuyingController):
|
|||||||
items = []
|
items = []
|
||||||
for d in obj.get("items"):
|
for d in obj.get("items"):
|
||||||
if not d.qty:
|
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))
|
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
# udpate with latest quantities
|
# udpate with latest quantities
|
||||||
@@ -56,11 +58,11 @@ class PurchaseCommon(BuyingController):
|
|||||||
d.set(x, f_lst[x])
|
d.set(x, f_lst[x])
|
||||||
|
|
||||||
item = frappe.db.sql("""select is_stock_item, is_purchase_item,
|
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]
|
d.item_code, as_dict=1)[0]
|
||||||
|
|
||||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
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
|
# validate stock item
|
||||||
if item.is_stock_item==1 and d.qty and not d.warehouse:
|
if item.is_stock_item==1 and d.qty and not d.warehouse:
|
||||||
@@ -72,16 +74,17 @@ class PurchaseCommon(BuyingController):
|
|||||||
frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx))
|
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))
|
items.append(cstr(d.item_code))
|
||||||
|
|
||||||
if items and len(items) != len(set(items)) and \
|
if items and len(items) != len(set(items)) and \
|
||||||
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
|
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
|
||||||
frappe.msgprint(_("Warning: Same item has been entered multiple times."))
|
frappe.msgprint(_("Warning: Same item has been entered multiple times."))
|
||||||
|
|
||||||
|
|
||||||
def check_for_stopped_status(self, doctype, docname):
|
def check_for_stopped_or_closed_status(self, doctype, docname):
|
||||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
status = frappe.db.get_value(doctype, docname, "status")
|
||||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
|
||||||
if stopped:
|
if status in ("Stopped", "Closed"):
|
||||||
frappe.throw(_("{0} {1} status is 'Stopped'").format(doctype, docname), frappe.InvalidStatusError)
|
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
|
||||||
|
|
||||||
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
|
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
|
||||||
if check == 'Next':
|
if check == 'Next':
|
||||||
|
|||||||
@@ -19,10 +19,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
this._super();
|
this._super();
|
||||||
// this.frm.dashboard.reset();
|
// 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)
|
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
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) {
|
if(flt(doc.per_billed)==0) {
|
||||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
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();
|
cur_frm.cscript.add_from_mappers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
|
||||||
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']);
|
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
make_stock_entry: function() {
|
make_stock_entry: function() {
|
||||||
@@ -146,15 +154,28 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
|
|
||||||
make_bank_entry: function() {
|
make_bank_entry: function() {
|
||||||
return frappe.call({
|
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: {
|
args: {
|
||||||
"purchase_order": cur_frm.doc.name
|
"dt": "Purchase Order",
|
||||||
|
"dn": cur_frm.doc.name
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
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
|
// for backward compatibility: combine new and previous states
|
||||||
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
$.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) {
|
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
|
||||||
return {
|
return {
|
||||||
filters: {'supplier': doc.supplier}
|
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){
|
cur_frm.pformat.indent_no = function(doc, cdt, cdn){
|
||||||
//function to make row of table
|
//function to make row of table
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "",
|
||||||
"fieldname": "address_display",
|
"fieldname": "address_display",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -326,6 +327,255 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -1479,7 +1729,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"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,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@@ -2033,7 +2283,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-16 06:13:50.058318",
|
"modified": "2015-11-04 04:44:22.025827",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
pc_obj = frappe.get_doc('Purchase Common')
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
pc_obj.validate_for_items(self)
|
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("uom", "qty")
|
||||||
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
|
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
|
||||||
@@ -108,12 +108,12 @@ class PurchaseOrder(BuyingController):
|
|||||||
= d.rate = item_last_purchase_rate
|
= d.rate = item_last_purchase_rate
|
||||||
|
|
||||||
# Check for Stopped status
|
# Check for Stopped status
|
||||||
def check_for_stopped_status(self, pc_obj):
|
def check_for_stopped_or_closed_status(self, pc_obj):
|
||||||
check_list =[]
|
check_list =[]
|
||||||
for d in self.get('items'):
|
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:
|
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)
|
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):
|
def update_requested_qty(self):
|
||||||
material_request_map = {}
|
material_request_map = {}
|
||||||
@@ -154,14 +154,16 @@ class PurchaseOrder(BuyingController):
|
|||||||
|
|
||||||
def update_status(self, status):
|
def update_status(self, status):
|
||||||
self.check_modified_date()
|
self.check_modified_date()
|
||||||
self.db_set('status', status)
|
self.set_status(update=True, status=status)
|
||||||
self.set_status(update=True)
|
|
||||||
self.update_requested_qty()
|
self.update_requested_qty()
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
self.notify_update()
|
self.notify_update()
|
||||||
clear_doctype_notifications(self)
|
clear_doctype_notifications(self)
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
if self.delivered_by_supplier == 1:
|
||||||
|
self.update_status_updater()
|
||||||
|
|
||||||
super(PurchaseOrder, self).on_submit()
|
super(PurchaseOrder, self).on_submit()
|
||||||
|
|
||||||
purchase_controller = frappe.get_doc("Purchase Common")
|
purchase_controller = frappe.get_doc("Purchase Common")
|
||||||
@@ -176,8 +178,11 @@ class PurchaseOrder(BuyingController):
|
|||||||
purchase_controller.update_last_purchase_rate(self, is_submit = 1)
|
purchase_controller.update_last_purchase_rate(self, is_submit = 1)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
if self.delivered_by_supplier == 1:
|
||||||
|
self.update_status_updater()
|
||||||
|
|
||||||
pc_obj = frappe.get_doc('Purchase Common')
|
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
|
# 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')
|
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"):
|
"prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"):
|
||||||
d.set(field, None)
|
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()
|
@frappe.whitelist()
|
||||||
def stop_or_unstop_purchase_orders(names, status):
|
def stop_or_unstop_purchase_orders(names, status):
|
||||||
if not frappe.has_permission("Purchase Order", "write"):
|
if not frappe.has_permission("Purchase Order", "write"):
|
||||||
@@ -223,11 +250,11 @@ def stop_or_unstop_purchase_orders(names, status):
|
|||||||
for name in names:
|
for name in names:
|
||||||
po = frappe.get_doc("Purchase Order", name)
|
po = frappe.get_doc("Purchase Order", name)
|
||||||
if po.docstatus == 1:
|
if po.docstatus == 1:
|
||||||
if status=="Stopped":
|
if status in ("Stopped", "Closed"):
|
||||||
if po.status not in ("Stopped", "Cancelled") and (po.per_received < 100 or po.per_billed < 100):
|
if po.status not in ("Stopped", "Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
|
||||||
po.update_status("Stopped")
|
po.update_status(status)
|
||||||
else:
|
else:
|
||||||
if po.status == "Stopped":
|
if po.status in ("Stopped", "Closed"):
|
||||||
po.update_status("Draft")
|
po.update_status("Draft")
|
||||||
|
|
||||||
frappe.local.message_log = []
|
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.bom_no = po_item.bom
|
||||||
stock_entry.get_items()
|
stock_entry.get_items()
|
||||||
return stock_entry.as_dict()
|
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,6 +4,10 @@ frappe.listview_settings['Purchase Order'] = {
|
|||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(doc.status==="Stopped") {
|
if(doc.status==="Stopped") {
|
||||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
return [__("Stopped"), "darkgrey", "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") {
|
}else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
|
||||||
if(flt(doc.per_billed, 2) < 100) {
|
if(flt(doc.per_billed, 2) < 100) {
|
||||||
return [__("To Receive and Bill"), "orange",
|
return [__("To Receive and Bill"), "orange",
|
||||||
@@ -21,13 +25,16 @@ frappe.listview_settings['Purchase Order'] = {
|
|||||||
onload: function(listview) {
|
onload: function(listview) {
|
||||||
var method = "erpnext.buying.doctype.purchase_order.purchase_order.stop_or_unstop_purchase_orders";
|
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.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"});
|
listview.call_for_selected_items(method, {"status": "Submitted"});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,6 +71,20 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
po = create_purchase_order(qty=3.4, do_not_save=True)
|
po = create_purchase_order(qty=3.4, do_not_save=True)
|
||||||
self.assertRaises(UOMMustBeIntegerError, po.insert)
|
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):
|
def create_purchase_order(**args):
|
||||||
po = frappe.new_doc("Purchase Order")
|
po = frappe.new_doc("Purchase Order")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@@ -848,15 +848,16 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "prevdoc_doctype",
|
"fieldname": "prevdoc_doctype",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Prevdoc DocType",
|
"label": "Reference Document Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "prevdoc_doctype",
|
"oldfieldname": "prevdoc_doctype",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
|
"options": "DocType",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@@ -871,16 +872,16 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "prevdoc_docname",
|
"fieldname": "prevdoc_docname",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Material Request No",
|
"label": "Reference Name",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "prevdoc_docname",
|
"oldfieldname": "prevdoc_docname",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Material Request",
|
"options": "prevdoc_doctype",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "120px",
|
"print_width": "120px",
|
||||||
@@ -1200,7 +1201,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-19 03:04:51.773011",
|
"modified": "2015-10-19 03:04:51.773012",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
|||||||
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"),
|
"label": _("Customer and Supplier"),
|
||||||
"youtube_id": "anoGi_RpQ20"
|
"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"),
|
"label": _("Lead to Quotation"),
|
||||||
"youtube_id": "TxYX4r4JAKA"
|
"youtube_id": "TxYX4r4JAKA"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "help",
|
||||||
|
"label": _("Newsletters"),
|
||||||
|
"youtube_id": "muLKsCrrDRo"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ def get_data():
|
|||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
"Learn": {
|
"Learn": {
|
||||||
"color": "#FCB868",
|
"color": "#FF888B",
|
||||||
"force_show": True,
|
"force_show": True,
|
||||||
"icon": "icon-facetime-video",
|
"icon": "octicon octicon-device-camera-video",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"is_help": True
|
"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"),
|
"label": _("Items and Pricing"),
|
||||||
"youtube_id": "qXaEwld4_Ps"
|
"youtube_id": "qXaEwld4_Ps"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "help",
|
||||||
|
"label": _("Item Variants"),
|
||||||
|
"youtube_id": "OGBETlCzU5o"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "help",
|
"type": "help",
|
||||||
"label": _("Opening Stock Balance"),
|
"label": _("Opening Stock Balance"),
|
||||||
|
|||||||
@@ -120,6 +120,16 @@ def get_data():
|
|||||||
"label": _("Bill of Materials"),
|
"label": _("Bill of Materials"),
|
||||||
"youtube_id": "hDV0c1OeWLo"
|
"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"),
|
"label": _("Customer and Supplier"),
|
||||||
"youtube_id": "anoGi_RpQ20"
|
"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"),
|
"label": _("Documents"),
|
||||||
"icon": "icon-star",
|
"icon": "icon-star",
|
||||||
"items": [
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Item",
|
||||||
|
"description": _("All Products or Services."),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Material Request",
|
"name": "Material Request",
|
||||||
@@ -32,11 +37,6 @@ def get_data():
|
|||||||
"name": "Installation Note",
|
"name": "Installation Note",
|
||||||
"description": _("Installation record for a Serial No.")
|
"description": _("Installation record for a Serial No.")
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Item",
|
|
||||||
"description": _("All Products or Services."),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Warehouse",
|
"name": "Warehouse",
|
||||||
@@ -263,6 +263,11 @@ def get_data():
|
|||||||
"label": _("Items and Pricing"),
|
"label": _("Items and Pricing"),
|
||||||
"youtube_id": "qXaEwld4_Ps"
|
"youtube_id": "qXaEwld4_Ps"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "help",
|
||||||
|
"label": _("Item Variants"),
|
||||||
|
"youtube_id": "OGBETlCzU5o"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "help",
|
"type": "help",
|
||||||
"label": _("Opening Stock Balance"),
|
"label": _("Opening Stock Balance"),
|
||||||
@@ -270,8 +275,23 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "help",
|
"type": "help",
|
||||||
"label": _("Item Variants"),
|
"label": _("Making Stock Entries"),
|
||||||
"youtube_id": "OGBETlCzU5o"
|
"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.utilities.transaction_base import TransactionBase
|
||||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||||
from erpnext.controllers.sales_and_purchase_return import validate_return
|
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
|
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
|
||||||
|
|
||||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||||
@@ -221,7 +221,7 @@ class AccountsController(TransactionBase):
|
|||||||
if not account_currency:
|
if not account_currency:
|
||||||
account_currency = get_account_currency(gl_dict.account)
|
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.validate_account_currency(gl_dict.account, account_currency)
|
||||||
self.set_balance_in_account_currency(gl_dict, 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}")
|
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||||
.format(party_type, party, party_account_currency), InvalidCurrency)
|
.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()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
return frappe.db.get_value("Account", account_head, "tax_rate")
|
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
|
from tabItem
|
||||||
where tabItem.docstatus < 2
|
where tabItem.docstatus < 2
|
||||||
and ifnull(tabItem.has_variants, 0)=0
|
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.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
|
||||||
and (tabItem.`{key}` LIKE %(txt)s
|
and (tabItem.`{key}` LIKE %(txt)s
|
||||||
or tabItem.item_name 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
|
return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
|
||||||
from `tabDelivery Note`
|
from `tabDelivery Note`
|
||||||
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
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
|
(ifnull((select sum(qty) from `tabDelivery Note Item` where
|
||||||
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
|
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
|
||||||
ifnull((select sum(qty) from `tabSales Invoice Item` where
|
ifnull((select sum(qty) from `tabSales Invoice Item` where
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ def validate_recurring_document(doc):
|
|||||||
raise_exception=1)
|
raise_exception=1)
|
||||||
|
|
||||||
elif not (doc.from_date and doc.to_date):
|
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):
|
def convert_to_recurring(doc, posting_date):
|
||||||
|
|||||||
@@ -219,12 +219,12 @@ class SellingController(StockController):
|
|||||||
so_warehouse = so_item and so_item[0]["warehouse"] or ""
|
so_warehouse = so_item and so_item[0]["warehouse"] or ""
|
||||||
return so_qty, so_warehouse
|
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"):
|
for d in self.get("items"):
|
||||||
if d.get(ref_fieldname):
|
if d.get(ref_fieldname):
|
||||||
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
|
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
|
||||||
if status == "Stopped":
|
if status in ("Stopped", "Closed"):
|
||||||
frappe.throw(_("Sales Order {0} is stopped").format(d.get(ref_fieldname)))
|
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
|
||||||
|
|
||||||
def check_active_sales_items(obj):
|
def check_active_sales_items(obj):
|
||||||
for d in obj.get("items"):
|
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"],
|
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
|
||||||
["Stopped", "eval:self.status=='Stopped'"],
|
["Stopped", "eval:self.status=='Stopped'"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
],
|
],
|
||||||
"Purchase Order": [
|
"Purchase Order": [
|
||||||
["Draft", None],
|
["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 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"],
|
["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"],
|
["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'"],
|
["Stopped", "eval:self.status=='Stopped'"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
],
|
],
|
||||||
"Delivery Note": [
|
"Delivery Note": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["Submitted", "eval:self.docstatus==1"],
|
["Submitted", "eval:self.docstatus==1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
],
|
],
|
||||||
"Purchase Receipt": [
|
"Purchase Receipt": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["Submitted", "eval:self.docstatus==1"],
|
["Submitted", "eval:self.docstatus==1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,12 +76,16 @@ class StatusUpdater(Document):
|
|||||||
self.update_qty()
|
self.update_qty()
|
||||||
self.validate_qty()
|
self.validate_qty()
|
||||||
|
|
||||||
def set_status(self, update=False):
|
def set_status(self, update=False, status=None):
|
||||||
if self.is_new():
|
if self.is_new():
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.doctype in status_map:
|
if self.doctype in status_map:
|
||||||
_status = self.status
|
_status = self.status
|
||||||
|
|
||||||
|
if status and update:
|
||||||
|
self.db_set("status", status)
|
||||||
|
|
||||||
sl = status_map[self.doctype][:]
|
sl = status_map[self.doctype][:]
|
||||||
sl.reverse()
|
sl.reverse()
|
||||||
for s in sl:
|
for s in sl:
|
||||||
@@ -168,7 +177,6 @@ class StatusUpdater(Document):
|
|||||||
else:
|
else:
|
||||||
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
|
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
|
||||||
|
|
||||||
args['set_modified'] = ''
|
|
||||||
if change_modified:
|
if change_modified:
|
||||||
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
|
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
|
||||||
.format(frappe.db.escape(frappe.session.user))
|
.format(frappe.db.escape(frappe.session.user))
|
||||||
@@ -252,9 +260,9 @@ class StatusUpdater(Document):
|
|||||||
zero_amount_refdoc.append(item.get(ref_fieldname))
|
zero_amount_refdoc.append(item.get(ref_fieldname))
|
||||||
|
|
||||||
if zero_amount_refdoc:
|
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:
|
for ref_dn in zero_amount_refdoc:
|
||||||
ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item`
|
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])
|
where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ blogs.
|
|||||||
"""
|
"""
|
||||||
app_icon = "icon-th"
|
app_icon = "icon-th"
|
||||||
app_color = "#e74c3c"
|
app_color = "#e74c3c"
|
||||||
app_version = "6.5.2"
|
app_version = "6.7.3"
|
||||||
github_link = "https://github.com/frappe/erpnext"
|
source_link = "https://github.com/frappe/erpnext"
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
|
||||||
|
|||||||
@@ -18,24 +18,25 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
|||||||
jv.voucher_type = 'Bank Entry';
|
jv.voucher_type = 'Bank Entry';
|
||||||
jv.company = cur_frm.doc.company;
|
jv.company = cur_frm.doc.company;
|
||||||
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
|
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
|
||||||
jv.fiscal_year = cur_frm.doc.fiscal_year;
|
|
||||||
var expense = cur_frm.doc.expenses || [];
|
var expense = cur_frm.doc.expenses || [];
|
||||||
for(var i = 0; i < expense.length; i++){
|
for(var i = 0; i < expense.length; i++){
|
||||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||||
d1.debit = expense[i].sanctioned_amount;
|
|
||||||
d1.account = expense[i].default_account;
|
d1.account = expense[i].default_account;
|
||||||
|
d1.debit_in_account_currency = expense[i].sanctioned_amount;
|
||||||
d1.reference_type = cur_frm.doc.doctype;
|
d1.reference_type = cur_frm.doc.doctype;
|
||||||
d1.reference_name = cur_frm.doc.name;
|
d1.reference_name = cur_frm.doc.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// credit to bank
|
// credit to bank
|
||||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
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_type = cur_frm.doc.doctype;
|
||||||
d1.reference_name = cur_frm.doc.name;
|
d1.reference_name = cur_frm.doc.name;
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
d1.account = r.message.account;
|
d1.account = r.message.account;
|
||||||
d1.balance = r.message.balance;
|
d1.balance = r.message.balance;
|
||||||
|
d1.account_currency = r.message.account_currency;
|
||||||
|
d1.account_type = r.message.account_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
loaddoc('Journal Entry', jv.name);
|
loaddoc('Journal Entry', jv.name);
|
||||||
|
|||||||
@@ -78,25 +78,22 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "leave_type",
|
"fieldname": "column_break1",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"label": "Leave Type",
|
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "leave_type",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Leave Type",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 0,
|
||||||
"search_index": 1,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0,
|
||||||
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@@ -126,62 +123,38 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "column_break1",
|
"fieldname": "section_break_6",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 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
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "leave_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"label": "Fiscal Year",
|
"label": "Leave Type",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "fiscal_year",
|
"oldfieldname": "leave_type",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Link",
|
||||||
"options": "Fiscal Year",
|
"options": "Leave Type",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@@ -195,19 +168,20 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "carry_forward",
|
"fieldname": "from_date",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Carry Forward",
|
"label": "From Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
@@ -216,18 +190,39 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "carry_forward",
|
"fieldname": "to_date",
|
||||||
"fieldname": "carry_forwarded_leaves",
|
"fieldtype": "Date",
|
||||||
"fieldtype": "Float",
|
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Carry Forwarded Leaves",
|
"label": "To Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"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,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@@ -255,6 +250,50 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -310,7 +349,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-02 07:38:55.314632",
|
"modified": "2015-11-04 03:13:11.121463",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Allocation",
|
"name": "Leave Allocation",
|
||||||
@@ -359,7 +398,7 @@
|
|||||||
],
|
],
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 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_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC"
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt, date_diff
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import set_employee_name
|
from erpnext.hr.utils import set_employee_name
|
||||||
|
|
||||||
class LeaveAllocation(Document):
|
class LeaveAllocation(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_period()
|
||||||
self.validate_new_leaves_allocated_value()
|
self.validate_new_leaves_allocated_value()
|
||||||
self.check_existing_leave_allocation()
|
self.check_existing_leave_allocation()
|
||||||
if not self.total_leaves_allocated:
|
if not self.total_leaves_allocated:
|
||||||
@@ -23,6 +24,10 @@ class LeaveAllocation(Document):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.get_total_allocated_leaves()
|
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):
|
def validate_new_leaves_allocated_value(self):
|
||||||
"""validate that leave allocation is in multiples of 0.5"""
|
"""validate that leave allocation is in multiples of 0.5"""
|
||||||
if flt(self.new_leaves_allocated) % 0.5:
|
if flt(self.new_leaves_allocated) % 0.5:
|
||||||
@@ -31,28 +36,29 @@ class LeaveAllocation(Document):
|
|||||||
def check_existing_leave_allocation(self):
|
def check_existing_leave_allocation(self):
|
||||||
"""check whether leave for same type is already allocated or not"""
|
"""check whether leave for same type is already allocated or not"""
|
||||||
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
|
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
|
||||||
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
|
where employee='%s' and leave_type='%s' and to_date >= '%s' and from_date <= '%s' and docstatus=1
|
||||||
(self.employee, self.leave_type, self.fiscal_year))
|
"""%(self.employee, self.leave_type, self.from_date, self.to_date))
|
||||||
|
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for Fiscal Year {0}").format(self.leave_type,
|
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for period {2} - {3}").format(self.leave_type,
|
||||||
self.employee, self.fiscal_year))
|
self.employee, self.from_date, self.to_date))
|
||||||
frappe.throw('<a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
|
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
|
||||||
|
|
||||||
def get_leave_bal(self, prev_fyear):
|
def get_leave_bal(self):
|
||||||
return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear)
|
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))
|
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
|
||||||
from `tabLeave Application` where employee=%s and leave_type=%s
|
from `tabLeave Application` where employee=%s and leave_type=%s
|
||||||
and fiscal_year=%s and docstatus=1""",
|
and to_date<=%s and docstatus=1""",
|
||||||
(self.employee, self.leave_type, fiscal_year))
|
(self.employee, self.leave_type, self.from_date))
|
||||||
return leaves_applied and flt(leaves_applied[0][0]) or 0
|
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))
|
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0))
|
||||||
from `tabLeave Allocation` where employee=%s and leave_type=%s
|
from `tabLeave Allocation` where employee=%s and leave_type=%s
|
||||||
and fiscal_year=%s and docstatus=1 and name!=%s""",
|
and to_date<=%s and docstatus=1 and name!=%s""",
|
||||||
(self.employee, self.leave_type, fiscal_year, self.name))
|
(self.employee, self.leave_type, self.from_date, self.name))
|
||||||
return leaves_allocated and flt(leaves_allocated[0][0]) or 0
|
return leaves_allocated and flt(leaves_allocated[0][0]) or 0
|
||||||
|
|
||||||
def allow_carry_forward(self):
|
def allow_carry_forward(self):
|
||||||
@@ -67,14 +73,11 @@ class LeaveAllocation(Document):
|
|||||||
def get_carry_forwarded_leaves(self):
|
def get_carry_forwarded_leaves(self):
|
||||||
if self.carry_forward:
|
if self.carry_forward:
|
||||||
self.allow_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
|
prev_bal = 0
|
||||||
if prev_fiscal_year and cint(self.carry_forward) == 1:
|
if cint(self.carry_forward) == 1:
|
||||||
prev_bal = self.get_leave_bal(prev_fiscal_year)
|
prev_bal = self.get_leave_bal()
|
||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
'carry_forwarded_leaves': prev_bal,
|
'carry_forwarded_leaves': prev_bal,
|
||||||
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
|
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
|
||||||
@@ -83,6 +86,10 @@ class LeaveAllocation(Document):
|
|||||||
|
|
||||||
def get_total_allocated_leaves(self):
|
def get_total_allocated_leaves(self):
|
||||||
leave_det = self.get_carry_forwarded_leaves()
|
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,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves']))
|
||||||
frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
|
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
|
from __future__ import unicode_literals
|
||||||
import frappe
|
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"]
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"doctype": "Leave Allocation",
|
"doctype": "Leave Allocation",
|
||||||
"employee": "_T-Employee-0001",
|
"employee": "_T-Employee-0001",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"from_date": "2013-01-01",
|
||||||
|
"to_date": "2013-12-31",
|
||||||
"leave_type": "_Test Leave Type",
|
"leave_type": "_Test Leave Type",
|
||||||
"new_leaves_allocated": 15
|
"new_leaves_allocated": 15
|
||||||
},
|
},
|
||||||
@@ -11,7 +12,8 @@
|
|||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"doctype": "Leave Allocation",
|
"doctype": "Leave Allocation",
|
||||||
"employee": "_T-Employee-0002",
|
"employee": "_T-Employee-0002",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"from_date": "2013-01-01",
|
||||||
|
"to_date": "2013-12-31",
|
||||||
"leave_type": "_Test Leave Type",
|
"leave_type": "_Test Leave Type",
|
||||||
"new_leaves_allocated": 15
|
"new_leaves_allocated": 15
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,114 +2,101 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
cur_frm.add_fetch('employee','employee_name','employee_name');
|
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) {
|
frappe.ui.form.on("Leave Application", {
|
||||||
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
|
onload: function(frm) {
|
||||||
});
|
if (!frm.doc.posting_date) {
|
||||||
|
frm.set_value("posting_date", get_today());
|
||||||
cur_frm.cscript.onload = function(doc, dt, dn) {
|
|
||||||
if(!doc.posting_date)
|
|
||||||
set_multiple(dt,dn,{posting_date:get_today()});
|
|
||||||
if(doc.__islocal) {
|
|
||||||
cur_frm.set_value("status", "Open");
|
|
||||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.set_query("leave_approver", function() {
|
frm.set_query("leave_approver", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
|
||||||
filters: {
|
filters: {
|
||||||
employee: cur_frm.doc.employee
|
employee: frm.doc.employee
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.cscript.get_leave_balance(cur_frm.doc);
|
frm.set_query("employee", erpnext.queries.employee);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.employee = function (doc, dt, dn){
|
refresh: function(frm) {
|
||||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
if (frm.is_new()) {
|
||||||
|
frm.set_value("status", "Open");
|
||||||
|
frm.trigger("calculate_total_days");
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
cur_frm.cscript.fiscal_year = function (doc, dt, dn){
|
leave_approver: function(frm) {
|
||||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
|
||||||
}
|
},
|
||||||
|
|
||||||
cur_frm.cscript.leave_type = function (doc, dt, dn){
|
employee: function(frm) {
|
||||||
cur_frm.cscript.get_leave_balance(doc, dt, dn);
|
frm.trigger("get_leave_balance");
|
||||||
}
|
},
|
||||||
|
|
||||||
cur_frm.cscript.half_day = function(doc, dt, dn) {
|
leave_type: function(frm) {
|
||||||
if(doc.from_date) {
|
frm.trigger("get_leave_balance");
|
||||||
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) {
|
half_day: function(frm) {
|
||||||
if(cint(doc.half_day) == 1){
|
if (frm.doc.from_date) {
|
||||||
set_multiple(dt,dn,{to_date:doc.from_date});
|
frm.set_value("to_date", frm.doc.from_date);
|
||||||
}
|
frm.trigger("calculate_total_days");
|
||||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
cur_frm.cscript.to_date = function(doc, dt, dn) {
|
from_date: function(frm) {
|
||||||
if(cint(doc.half_day) == 1 && cstr(doc.from_date) && doc.from_date != doc.to_date){
|
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"));
|
msgprint(__("To Date should be same as From Date for Half Day leave"));
|
||||||
set_multiple(dt,dn,{to_date:doc.from_date});
|
frm.set_value("to_date", frm.doc.from_date);
|
||||||
}
|
|
||||||
cur_frm.cscript.calculate_total_days(doc, dt, dn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.get_leave_balance = function(doc, dt, dn) {
|
frm.trigger("calculate_total_days");
|
||||||
if(doc.docstatus==0 && doc.employee && doc.leave_type && doc.fiscal_year) {
|
},
|
||||||
return cur_frm.call({
|
|
||||||
|
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",
|
method: "get_leave_balance",
|
||||||
args: {
|
args: {
|
||||||
employee: doc.employee,
|
employee: frm.doc.employee,
|
||||||
fiscal_year: doc.fiscal_year,
|
from_date: frm.doc.from_date,
|
||||||
leave_type: doc.leave_type
|
to_date: frm.doc.to_date,
|
||||||
|
leave_type: frm.doc.leave_type
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
cur_frm.cscript.calculate_total_days = function(doc, dt, dn) {
|
calculate_total_days: function(frm) {
|
||||||
if(doc.from_date && doc.to_date){
|
if(frm.doc.from_date && frm.doc.to_date) {
|
||||||
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
|
if (cint(frm.doc.half_day)==1) {
|
||||||
else{
|
frm.set_value("total_leave_days", 0.5);
|
||||||
|
} else {
|
||||||
// server call is done to include holidays in leave days calculations
|
// server call is done to include holidays in leave days calculations
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
|
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
|
||||||
args: {leave_app: doc},
|
args: { leave_app: frm.doc },
|
||||||
callback: function(response) {
|
callback: function(response) {
|
||||||
if (response && response.message) {
|
if (response && response.message) {
|
||||||
cur_frm.set_value('total_leave_days', response.message.total_leave_days);
|
frm.set_value('total_leave_days', response.message.total_leave_days);
|
||||||
|
frm.trigger("get_leave_balance");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
cur_frm.fields_dict.employee.get_query = erpnext.queries.employee;
|
});
|
||||||
|
|||||||
@@ -559,7 +559,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 3,
|
"max_attachments": 3,
|
||||||
"modified": "2015-10-02 07:38:55.471712",
|
"modified": "2015-10-28 16:14:25.640730",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Application",
|
"name": "Leave Application",
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class LeaveApplication(Document):
|
|||||||
|
|
||||||
if not is_lwp(self.leave_type):
|
if not is_lwp(self.leave_type):
|
||||||
self.leave_balance = get_leave_balance(self.employee,
|
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" \
|
if self.status != "Rejected" \
|
||||||
and self.leave_balance - self.total_leave_days < 0:
|
and self.leave_balance - self.total_leave_days < 0:
|
||||||
@@ -122,9 +122,8 @@ class LeaveApplication(Document):
|
|||||||
employee = %(employee)s
|
employee = %(employee)s
|
||||||
and docstatus < 2
|
and docstatus < 2
|
||||||
and status in ("Open", "Approved")
|
and status in ("Open", "Approved")
|
||||||
and (from_date between %(from_date)s and %(to_date)s
|
and to_date >= %(from_date)s
|
||||||
or to_date between %(from_date)s and %(to_date)s
|
and from_date <= %(to_date)s
|
||||||
or %(from_date)s between from_date and to_date)
|
|
||||||
and name != %(name)s""", {
|
and name != %(name)s""", {
|
||||||
"employee": self.employee,
|
"employee": self.employee,
|
||||||
"from_date": self.from_date,
|
"from_date": self.from_date,
|
||||||
@@ -251,18 +250,18 @@ def get_total_leave_days(leave_app):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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
|
leave_all = frappe.db.sql("""select total_leaves_allocated
|
||||||
from `tabLeave Allocation` where employee = %s and leave_type = %s
|
from `tabLeave Allocation` where employee = %s and leave_type = %s
|
||||||
and fiscal_year = %s and docstatus = 1""", (employee,
|
and from_date<=%s and to_date>=%s and docstatus = 1""", (employee,
|
||||||
leave_type, fiscal_year))
|
leave_type, from_date, to_date))
|
||||||
|
|
||||||
leave_all = leave_all and flt(leave_all[0][0]) or 0
|
leave_all = leave_all and flt(leave_all[0][0]) or 0
|
||||||
|
|
||||||
leave_app = frappe.db.sql("""select SUM(total_leave_days)
|
leave_app = frappe.db.sql("""select SUM(total_leave_days)
|
||||||
from `tabLeave Application`
|
from `tabLeave Application`
|
||||||
where employee = %s and leave_type = %s and fiscal_year = %s
|
where employee = %s and leave_type = %s and to_date>=%s and from_date<=%s
|
||||||
and status="Approved" and docstatus = 1""", (employee, leave_type, fiscal_year))
|
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
|
leave_app = leave_app and flt(leave_app[0][0]) or 0
|
||||||
|
|
||||||
ret = {'leave_balance': leave_all - leave_app}
|
ret = {'leave_balance': leave_all - leave_app}
|
||||||
|
|||||||
@@ -145,16 +145,38 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "from_date",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Fiscal Year",
|
"label": "From Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Fiscal Year",
|
|
||||||
"permlevel": 0,
|
"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,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@@ -260,7 +282,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-06-05 11:38:19.994852",
|
"modified": "2015-10-28 16:23:57.733900",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Control Panel",
|
"name": "Leave Control Panel",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
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 import msgprint, _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
@@ -27,10 +27,14 @@ class LeaveControlPanel(Document):
|
|||||||
return e
|
return e
|
||||||
|
|
||||||
def validate_values(self):
|
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):
|
if not self.get(f):
|
||||||
frappe.throw(_("{0} is required").format(self.meta.get_label(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):
|
def allocate_leave(self):
|
||||||
self.validate_values()
|
self.validate_values()
|
||||||
leave_allocated_for = []
|
leave_allocated_for = []
|
||||||
@@ -45,8 +49,8 @@ class LeaveControlPanel(Document):
|
|||||||
la.employee = cstr(d[0])
|
la.employee = cstr(d[0])
|
||||||
la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
|
la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
|
||||||
la.leave_type = self.leave_type
|
la.leave_type = self.leave_type
|
||||||
la.fiscal_year = self.fiscal_year
|
la.from_date = self.from_date
|
||||||
la.posting_date = nowdate()
|
la.to_date = self.to_date
|
||||||
la.carry_forward = cint(self.carry_forward)
|
la.carry_forward = cint(self.carry_forward)
|
||||||
la.new_leaves_allocated = flt(self.no_of_days)
|
la.new_leaves_allocated = flt(self.no_of_days)
|
||||||
la.docstatus = 1
|
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.submit_salary_slip = function(doc, cdt, cdn) {
|
||||||
cur_frm.cscript.display_activity_log("");
|
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
|
// clear all in locals
|
||||||
if(locals["Salary Slip"]) {
|
if(locals["Salary Slip"]) {
|
||||||
$.each(locals["Salary Slip"], function(name, d) {
|
$.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);
|
return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){
|
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){
|
||||||
|
|||||||
@@ -4,11 +4,16 @@
|
|||||||
frappe.query_reports["Employee Leave Balance"] = {
|
frappe.query_reports["Employee Leave Balance"] = {
|
||||||
"filters": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"fieldname":"fiscal_year",
|
"fieldname":"from_date",
|
||||||
"label": __("Fiscal Year"),
|
"label": __("From Date"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Date",
|
||||||
"options": "Fiscal Year",
|
"default": frappe.datetime.year_start()
|
||||||
"default": frappe.defaults.get_user_default("fiscal_year")
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.year_end()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"company",
|
"fieldname":"company",
|
||||||
|
|||||||
@@ -24,50 +24,45 @@ def execute(filters=None):
|
|||||||
|
|
||||||
leave_types = frappe.db.sql_list("select name from `tabLeave Type`")
|
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]
|
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`
|
from `tabLeave Allocation`
|
||||||
where docstatus=1 and employee in (%s)""" %
|
where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
|
||||||
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
|
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
|
||||||
|
filters.get("to_date")), employee_names, as_dict=True)
|
||||||
|
|
||||||
applications = frappe.db.sql("""select employee, fiscal_year, leave_type,
|
applications = frappe.db.sql("""select employee, leave_type,
|
||||||
SUM(total_leave_days) as leaves
|
SUM(total_leave_days) as leaves
|
||||||
from `tabLeave Application`
|
from `tabLeave Application`
|
||||||
where status="Approved" and docstatus = 1 and employee in (%s)
|
where status="Approved" and docstatus = 1 and employee in (%s)
|
||||||
group by employee, fiscal_year, leave_type""" %
|
and from_date >= '%s' and to_date <= '%s'
|
||||||
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
|
group by employee, leave_type""" %
|
||||||
|
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
|
||||||
|
filters.get("to_date")), employee_names, as_dict=True)
|
||||||
|
|
||||||
columns = [
|
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:
|
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) + " " + _("Taken") + ":Float")
|
||||||
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
|
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
for d in allocations:
|
for d in allocations:
|
||||||
data.setdefault((d.fiscal_year, d.employee,
|
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
|
||||||
d.leave_type), frappe._dict()).allocation = d.total_leaves_allocated
|
|
||||||
|
|
||||||
for d in applications:
|
for d in applications:
|
||||||
data.setdefault((d.fiscal_year, d.employee,
|
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
|
||||||
d.leave_type), frappe._dict()).leaves = d.leaves
|
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for fiscal_year in fiscal_years:
|
|
||||||
for employee in employees:
|
for employee in employees:
|
||||||
row = [fiscal_year, employee.name, employee.employee_name, employee.department]
|
row = [employee.name, employee.employee_name, employee.department]
|
||||||
result.append(row)
|
result.append(row)
|
||||||
for leave_type in leave_types:
|
for leave_type in leave_types:
|
||||||
tmp = data.get((fiscal_year, employee.name, leave_type), frappe._dict())
|
tmp = data.get((employee.name, leave_type), frappe._dict())
|
||||||
row.append(tmp.allocation or 0)
|
row.append(tmp.allocation or 0)
|
||||||
row.append(tmp.leaves or 0)
|
row.append(tmp.leaves or 0)
|
||||||
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
|
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ erpnext.production_order = {
|
|||||||
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'],
|
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'],
|
||||||
"icon-exclamation", "btn-default");
|
"icon-exclamation", "btn-default");
|
||||||
} else if (doc.status == 'Stopped') {
|
} 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");
|
"icon-check", "btn-default");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,18 +239,11 @@ $.extend(cur_frm.cscript, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.cscript['Stop Production Order'] = function() {
|
cur_frm.cscript['Stop Production Order'] = function() {
|
||||||
var doc = cur_frm.doc;
|
$c_obj(cur_frm.doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
|
||||||
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();});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript['Unstop Production Order'] = function() {
|
cur_frm.cscript['Unstop Production Order'] = function() {
|
||||||
var doc = cur_frm.doc;
|
$c_obj(cur_frm.doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
|
||||||
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();});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript['Transfer Raw Materials'] = function() {
|
cur_frm.cscript['Transfer Raw Materials'] = function() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
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 import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||||
@@ -342,8 +342,8 @@ class ProductionOrder(Document):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_item_details(item):
|
def get_item_details(item):
|
||||||
res = frappe.db.sql("""select stock_uom, description
|
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())
|
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""", item, as_dict=1)
|
and name=%s""", (nowdate(), item), as_dict=1)
|
||||||
if not res:
|
if not res:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,11 @@ class TestProductionOrder(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.ValidationError, prod_order.save)
|
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", "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)
|
prod_order = make_prod_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True)
|
||||||
self.assertRaises(ItemHasVariantError, prod_order.save)
|
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", "Applicable Territory")
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
|
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
|
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.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_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']:
|
if outgoing and outgoing['mail_server'] and outgoing['mail_login']:
|
||||||
account = frappe.new_doc("Email Account")
|
account = frappe.new_doc("Email Account")
|
||||||
mapping = {
|
mapping = {
|
||||||
"email_id": "mail_login",
|
"login_id_is_different": 1,
|
||||||
|
"email_id": "auto_email_id",
|
||||||
|
"login_id": "mail_login",
|
||||||
"password": "mail_password",
|
"password": "mail_password",
|
||||||
"footer": "footer",
|
"footer": "footer",
|
||||||
"smtp_server": "mail_server",
|
"smtp_server": "mail_server",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
@@ -11,9 +13,12 @@ def execute():
|
|||||||
for m in frappe.get_all("Project Milestone", "*"):
|
for m in frappe.get_all("Project Milestone", "*"):
|
||||||
if (m.milestone and m.milestone_date
|
if (m.milestone and m.milestone_date
|
||||||
and frappe.db.exists("Project", m.parent)):
|
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({
|
task = frappe.get_doc({
|
||||||
"doctype": "Task",
|
"doctype": "Task",
|
||||||
"subject": m.milestone,
|
"subject": subject,
|
||||||
|
"description": description if description!=subject else None,
|
||||||
"expected_start_date": m.milestone_date,
|
"expected_start_date": m.milestone_date,
|
||||||
"status": "Open" if m.status=="Pending" else "Closed",
|
"status": "Open" if m.status=="Pending" else "Closed",
|
||||||
"project": m.parent,
|
"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 = frappe.get_doc("File", unlinked_files[file_url]["file"])
|
||||||
file_data.attached_to_doctype = "Item"
|
file_data.attached_to_doctype = "Item"
|
||||||
file_data.attached_to_name = item_code
|
file_data.attached_to_name = item_code
|
||||||
|
file_data.flags.ignore_folder_validate = True
|
||||||
|
|
||||||
|
try:
|
||||||
file_data.save()
|
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
|
# set it as image in Item
|
||||||
if not frappe.db.get_value("Item", item_code, "image"):
|
if not frappe.db.get_value("Item", item_code, "image"):
|
||||||
|
|||||||
@@ -30,3 +30,6 @@ def execute():
|
|||||||
|
|
||||||
frappe.reload_doctype("Sales Invoice")
|
frappe.reload_doctype("Sales Invoice")
|
||||||
frappe.db.sql("""update `tabSales Invoice` set title = customer_name""")
|
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():
|
def execute():
|
||||||
frappe.reload_doctype("File")
|
frappe.reload_doctype("File")
|
||||||
frappe.reload_doctype("Item")
|
frappe.reload_doctype("Item")
|
||||||
for item in frappe.get_all("Item", fields=("name", "website_image")):
|
for item in frappe.get_all("Item", fields=("name", "website_image", "thumbnail")):
|
||||||
if item.website_image:
|
if item.website_image and not item.thumbnail:
|
||||||
item_doc = frappe.get_doc("Item", item.name)
|
item_doc = frappe.get_doc("Item", item.name)
|
||||||
try:
|
try:
|
||||||
item_doc.make_thumbnail()
|
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({
|
d.update({
|
||||||
"hours": tl.hours,
|
"hours": tl.hours,
|
||||||
"activity_type": tl.activity_type,
|
"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):
|
def validate_time_log_is_submitted(self, tl):
|
||||||
|
|||||||
@@ -115,6 +115,27 @@
|
|||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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,
|
"hide_heading": 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(),
|
var today = get_today(),
|
||||||
currency = frappe.defaults.get_user_default("currency");
|
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({
|
$.each({
|
||||||
posting_date: today,
|
posting_date: today,
|
||||||
transaction_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
|
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
|
||||||
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
|
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
|
||||||
this.apply_default_taxes();
|
this.apply_default_taxes();
|
||||||
}
|
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
|
||||||
|
&& !this.frm.doc.is_pos) {
|
||||||
if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"] && !this.frm.doc.is_pos) {
|
me.calculate_taxes_and_totals();
|
||||||
this.calculate_taxes_and_totals();
|
|
||||||
}
|
}
|
||||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||||
cur_frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
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);
|
me.frm.doc.name);
|
||||||
|
|
||||||
if(taxes_and_charges_field) {
|
if(taxes_and_charges_field) {
|
||||||
frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.controllers.accounts_controller.get_default_taxes_and_charges",
|
method: "erpnext.controllers.accounts_controller.get_default_taxes_and_charges",
|
||||||
args: {
|
args: {
|
||||||
"master_doctype": taxes_and_charges_field.options
|
"master_doctype": taxes_and_charges_field.options
|
||||||
@@ -104,6 +109,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
me.frm.set_value("taxes", r.message);
|
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() {
|
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);
|
erpnext.get_fiscal_year(this.frm.doc.company, this.frm.doc.transaction_date);
|
||||||
},
|
},
|
||||||
|
|
||||||
posting_date: function() {
|
posting_date: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
if (this.frm.doc.posting_date) {
|
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) ||
|
if ((this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.customer) ||
|
||||||
(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.supplier)) {
|
(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.supplier)) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
@@ -350,7 +362,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_exchange_rate: function(from_currency, to_currency, callback) {
|
get_exchange_rate: function(from_currency, to_currency, callback) {
|
||||||
frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.setup.utils.get_exchange_rate",
|
method: "erpnext.setup.utils.get_exchange_rate",
|
||||||
args: {
|
args: {
|
||||||
from_currency: from_currency,
|
from_currency: from_currency,
|
||||||
@@ -723,7 +735,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
var valid = true;
|
var valid = true;
|
||||||
|
|
||||||
$.each(["company", "customer"], function(i, fieldname) {
|
$.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]) {
|
if (!me.frm.doc[fieldname]) {
|
||||||
msgprint(__("Please specify") + ": " +
|
msgprint(__("Please specify") + ": " +
|
||||||
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
|
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";
|
method = "erpnext.accounts.party.get_party_details";
|
||||||
}
|
}
|
||||||
if(!args) {
|
if(!args) {
|
||||||
if(frm.doc.customer) {
|
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
|
||||||
args = {
|
args = {
|
||||||
party: frm.doc.customer,
|
party: frm.doc.customer,
|
||||||
party_type: "Customer",
|
party_type: "Customer",
|
||||||
@@ -22,11 +22,16 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (args) {
|
if (args) {
|
||||||
args.posting_date = frm.doc.transaction_date;
|
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!args) return;
|
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.currency = frm.doc.currency;
|
||||||
args.company = frm.doc.company;
|
args.company = frm.doc.company;
|
||||||
args.doctype = frm.doc.doctype;
|
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(frm.updating_party_details) return;
|
||||||
|
|
||||||
if(!address_field) {
|
if(!address_field) {
|
||||||
if(frm.doc.customer) {
|
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
|
||||||
address_field = "customer_address";
|
address_field = "customer_address";
|
||||||
} else if(frm.doc.supplier) {
|
} else if(frm.doc.supplier) {
|
||||||
address_field = "supplier_address";
|
address_field = "supplier_address";
|
||||||
@@ -64,6 +69,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
|
|||||||
if(r.message){
|
if(r.message){
|
||||||
frm.set_value(display_field, 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({
|
frappe.call({
|
||||||
method: "erpnext.accounts.party.set_taxes",
|
method: "erpnext.accounts.party.set_taxes",
|
||||||
args: {
|
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",
|
source_doctype: "Delivery Note",
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
status: ["!=", "Stopped"],
|
status: ["not in", ["Stopped", "Closed"]],
|
||||||
per_installed: ["<", 99.99],
|
per_installed: ["<", 99.99],
|
||||||
customer: cur_frm.doc.customer || undefined,
|
customer: cur_frm.doc.customer || undefined,
|
||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
|
|||||||
@@ -50,14 +50,6 @@ class InstallationNote(TransactionBase):
|
|||||||
if not frappe.db.exists("Serial No", x):
|
if not frappe.db.exists("Serial No", x):
|
||||||
frappe.throw(_("Serial No {0} does not exist").format(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):
|
def get_prevdoc_serial_no(self, prevdoc_detail_docname):
|
||||||
serial_nos = frappe.db.get_value("Delivery Note Item",
|
serial_nos = frappe.db.get_value("Delivery Note Item",
|
||||||
prevdoc_detail_docname, "serial_no")
|
prevdoc_detail_docname, "serial_no")
|
||||||
@@ -80,7 +72,6 @@ class InstallationNote(TransactionBase):
|
|||||||
if prevdoc_s_no:
|
if prevdoc_s_no:
|
||||||
self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
|
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):
|
def validate_installation_date(self):
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
@@ -102,11 +93,5 @@ class InstallationNote(TransactionBase):
|
|||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
|
|
||||||
def on_cancel(self):
|
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()
|
self.update_prevdoc_status()
|
||||||
frappe.db.set(self, 'status', 'Cancelled')
|
frappe.db.set(self, 'status', 'Cancelled')
|
||||||
|
|||||||
@@ -15,26 +15,45 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
this._super();
|
this._super();
|
||||||
this.frm.dashboard.reset();
|
this.frm.dashboard.reset();
|
||||||
|
var is_delivered_by_supplier = false;
|
||||||
|
var is_delivery_note = false;
|
||||||
|
|
||||||
if(doc.docstatus==1) {
|
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"),
|
$.each(cur_frm.doc.items, function(i, item){
|
||||||
// doc.per_delivered);
|
if(item.delivered_by_supplier == 1 || item.supplier){
|
||||||
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
if(item.qty > flt(item.ordered_qty))
|
||||||
// doc.per_billed);
|
is_delivered_by_supplier = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(item.qty > flt(item.delivered_qty))
|
||||||
|
is_delivery_note = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// indent
|
// material request
|
||||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
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);
|
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) {
|
if(flt(doc.per_billed)==0) {
|
||||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop
|
// stop
|
||||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'])
|
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cur_frm.add_custom_button(__('Close'), this.close_sales_order)
|
||||||
|
|
||||||
// maintenance
|
// maintenance
|
||||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||||
@@ -43,17 +62,19 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delivery note
|
// delivery note
|
||||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
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");
|
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
|
||||||
|
}
|
||||||
|
|
||||||
// sales invoice
|
// sales invoice
|
||||||
if(flt(doc.per_billed, 2) < 100) {
|
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(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// un-stop
|
// 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() {
|
make_bank_entry: function() {
|
||||||
return frappe.call({
|
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: {
|
args: {
|
||||||
"sales_order": cur_frm.doc.name
|
"dt": "Sales Order",
|
||||||
|
"dn": cur_frm.doc.name
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
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 doc = cur_frm.doc;
|
||||||
|
frappe.ui.form.is_saving = true;
|
||||||
var check = confirm(__("Are you sure you want to STOP ") + doc.name);
|
frappe.call({
|
||||||
|
method: "erpnext.selling.doctype.sales_order.sales_order.update_status",
|
||||||
if (check) {
|
args: {status: status, name: doc.name},
|
||||||
return $c('runserverobj', {
|
callback: function(r){
|
||||||
'method':'stop_sales_order',
|
cur_frm.reload_doc();
|
||||||
'docs': doc
|
},
|
||||||
}, function(r,rt) {
|
always: function() {
|
||||||
cur_frm.refresh();
|
frappe.ui.form.is_saving = false;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript['Unstop Sales Order'] = function() {
|
cur_frm.cscript['Unstop Sales Order'] = function() {
|
||||||
var doc = cur_frm.doc;
|
cur_frm.cscript.update_status('Re-open', 'Draft')
|
||||||
|
|
||||||
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.on_submit = function(doc, cdt, cdn) {
|
cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||||
|
|||||||
@@ -234,6 +234,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"default": "Sales",
|
"default": "Sales",
|
||||||
|
"depends_on": "",
|
||||||
"fieldname": "order_type",
|
"fieldname": "order_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -381,6 +382,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
@@ -1977,7 +1979,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"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,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@@ -2553,7 +2555,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-03 07:39:10.525609",
|
"modified": "2015-10-22 16:32:34.339835",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order",
|
"name": "Sales Order",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class SalesOrder(SellingController):
|
|||||||
self.validate_uom_is_integer("stock_uom", "qty")
|
self.validate_uom_is_integer("stock_uom", "qty")
|
||||||
self.validate_for_items()
|
self.validate_for_items()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
|
self.validate_drop_ship()
|
||||||
|
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
make_packing_list(self)
|
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
|
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))) \
|
(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),
|
frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
|
||||||
WarehouseRequired)
|
WarehouseRequired)
|
||||||
|
|
||||||
@@ -147,6 +148,11 @@ class SalesOrder(SellingController):
|
|||||||
doc.set_status(update=True)
|
doc.set_status(update=True)
|
||||||
doc.update_opportunity()
|
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):
|
def on_submit(self):
|
||||||
super(SalesOrder, self).on_submit()
|
super(SalesOrder, self).on_submit()
|
||||||
|
|
||||||
@@ -214,20 +220,13 @@ class SalesOrder(SellingController):
|
|||||||
if date_diff and date_diff[0][0]:
|
if date_diff and date_diff[0][0]:
|
||||||
frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name))
|
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.check_modified_date()
|
||||||
self.db_set('status', 'Stopped')
|
self.set_status(update=True, status=status)
|
||||||
self.update_reserved_qty()
|
self.update_reserved_qty()
|
||||||
self.notify_update()
|
self.notify_update()
|
||||||
clear_doctype_notifications(self)
|
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):
|
def update_reserved_qty(self, so_item_rows=None):
|
||||||
"""update requested qty (before ordered_qty is updated)"""
|
"""update requested qty (before ordered_qty is updated)"""
|
||||||
item_wh_list = []
|
item_wh_list = []
|
||||||
@@ -253,6 +252,46 @@ class SalesOrder(SellingController):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
pass
|
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):
|
def get_list_context(context=None):
|
||||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||||
list_context = get_list_context(context)
|
list_context = get_list_context(context)
|
||||||
@@ -268,12 +307,12 @@ def stop_or_unstop_sales_orders(names, status):
|
|||||||
for name in names:
|
for name in names:
|
||||||
so = frappe.get_doc("Sales Order", name)
|
so = frappe.get_doc("Sales Order", name)
|
||||||
if so.docstatus == 1:
|
if so.docstatus == 1:
|
||||||
if status=="Stop":
|
if status in ("Stopped", "Closed"):
|
||||||
if so.status not in ("Stopped", "Cancelled") and (so.per_delivered < 100 or so.per_billed < 100):
|
if so.status not in ("Stopped", "Cancelled", "Closed") and (so.per_delivered < 100 or so.per_billed < 100):
|
||||||
so.stop_sales_order()
|
so.update_status(status)
|
||||||
else:
|
else:
|
||||||
if so.status == "Stopped":
|
if so.status in ("Stopped", "Closed"):
|
||||||
so.unstop_sales_order()
|
so.update_status('Draft')
|
||||||
|
|
||||||
frappe.local.message_log = []
|
frappe.local.message_log = []
|
||||||
|
|
||||||
@@ -350,7 +389,7 @@ def make_delivery_note(source_name, target_doc=None):
|
|||||||
"parent": "against_sales_order",
|
"parent": "against_sales_order",
|
||||||
},
|
},
|
||||||
"postprocess": update_item,
|
"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": {
|
"Sales Taxes and Charges": {
|
||||||
"doctype": "Sales Taxes and Charges",
|
"doctype": "Sales Taxes and Charges",
|
||||||
@@ -488,3 +527,96 @@ def get_events(start, end, filters=None):
|
|||||||
"end": end
|
"end": end
|
||||||
}, as_dict=True, update={"allDay": 0})
|
}, as_dict=True, update={"allDay": 0})
|
||||||
return data
|
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") {
|
if(doc.status==="Stopped") {
|
||||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
||||||
|
|
||||||
|
} else if(doc.status==="Closed"){
|
||||||
|
return [__("Closed"), "green", "status,=,Closed"];
|
||||||
|
|
||||||
} else if (doc.order_type !== "Maintenance"
|
} else if (doc.order_type !== "Maintenance"
|
||||||
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||||
// to bill & overdue
|
// to bill & overdue
|
||||||
@@ -41,11 +44,15 @@ frappe.listview_settings['Sales Order'] = {
|
|||||||
onload: function(listview) {
|
onload: function(listview) {
|
||||||
var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders";
|
var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders";
|
||||||
|
|
||||||
listview.page.add_menu_item(__("Set as Stopped"), function() {
|
listview.page.add_menu_item(__("Close"), function() {
|
||||||
listview.call_for_selected_items(method, {"status": "Stop"});
|
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"});
|
listview.call_for_selected_items(method, {"status": "Unstop"});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import frappe.permissions
|
|||||||
import unittest
|
import unittest
|
||||||
from erpnext.selling.doctype.sales_order.sales_order \
|
from erpnext.selling.doctype.sales_order.sales_order \
|
||||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
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
|
from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||||
|
|
||||||
@@ -97,12 +95,12 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
# stop so
|
# stop so
|
||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
so.stop_sales_order()
|
so.update_status("Stopped")
|
||||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
|
||||||
|
|
||||||
# unstop so
|
# unstop so
|
||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
so.unstop_sales_order()
|
so.update_status('Draft')
|
||||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
|
||||||
|
|
||||||
dn.cancel()
|
dn.cancel()
|
||||||
@@ -147,14 +145,14 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
# stop so
|
# stop so
|
||||||
so.load_from_db()
|
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"), existing_reserved_qty_item1)
|
||||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2)
|
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2)
|
||||||
|
|
||||||
# unstop so
|
# unstop so
|
||||||
so.load_from_db()
|
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"), existing_reserved_qty_item1 + 25)
|
||||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
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)
|
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):
|
def make_sales_order(**args):
|
||||||
so = frappe.new_doc("Sales Order")
|
so = frappe.new_doc("Sales Order")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@@ -312,6 +426,11 @@ def make_sales_order(**args):
|
|||||||
if "warehouse" not in args:
|
if "warehouse" not in args:
|
||||||
args.warehouse = "_Test Warehouse - _TC"
|
args.warehouse = "_Test Warehouse - _TC"
|
||||||
|
|
||||||
|
if args.item_list:
|
||||||
|
for item in args.item_list:
|
||||||
|
so.append("items", item)
|
||||||
|
|
||||||
|
else:
|
||||||
so.append("items", {
|
so.append("items", {
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse,
|
"warehouse": args.warehouse,
|
||||||
|
|||||||
@@ -683,6 +683,74 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 1,
|
||||||
|
"collapsible_depends_on": "eval:doc.delivered_by_supplier==1||doc.supplier",
|
||||||
|
"fieldname": "drop_ship_section",
|
||||||
|
"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": 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": "delivered_by_supplier",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Supplier delivers to Customer",
|
||||||
|
"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": 1,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Supplier",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Supplier",
|
||||||
|
"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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -708,6 +776,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.delivered_by_supplier!=1",
|
||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -734,6 +803,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.delivered_by_supplier!=1",
|
||||||
"fieldname": "target_warehouse",
|
"fieldname": "target_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -917,6 +987,28 @@
|
|||||||
"unique": 0,
|
"unique": 0,
|
||||||
"width": "70px"
|
"width": "70px"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "ordered_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Ordered Qty",
|
||||||
|
"no_copy": 1,
|
||||||
|
"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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@@ -1095,7 +1187,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-19 03:04:51.257808",
|
"modified": "2015-11-04 11:29:57.645383",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order Item",
|
"name": "Sales Order Item",
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ def delete_company_transactions(company_name):
|
|||||||
|
|
||||||
for doctype in frappe.db.sql_list("""select parent from
|
for doctype in frappe.db.sql_list("""select parent from
|
||||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account", "Employee"):
|
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)
|
delete_for_doctype(doctype, company_name)
|
||||||
|
|
||||||
# Clear notification counts
|
# Clear notification counts
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ class EmailDigest(Document):
|
|||||||
"""Set standard digest style"""
|
"""Set standard digest style"""
|
||||||
context.text_muted = '#8D99A6'
|
context.text_muted = '#8D99A6'
|
||||||
context.text_color = '#36414C'
|
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};
|
context.label_css = '''display: inline-block; color: {text_muted};
|
||||||
padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
|
padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
|
||||||
context.section_head = 'margin-top: 60px; font-size: 16px;'
|
context.section_head = 'margin-top: 60px; font-size: 16px;'
|
||||||
@@ -139,7 +140,7 @@ class EmailDigest(Document):
|
|||||||
|
|
||||||
for i, e in enumerate(events):
|
for i, e in enumerate(events):
|
||||||
e.starts_on_label = format_time(e.starts_on)
|
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.date = formatdate(e.starts)
|
||||||
e.link = get_url_to_form("Event", e.name)
|
e.link = get_url_to_form("Event", e.name)
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ class EmailDigest(Document):
|
|||||||
|
|
||||||
todo_list = frappe.db.sql("""select *
|
todo_list = frappe.db.sql("""select *
|
||||||
from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open"
|
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)
|
(user_id, user_id), as_dict=True)
|
||||||
|
|
||||||
for t in todo_list:
|
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",
|
for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
|
||||||
"invoiced_amount", "payables", "bank_balance"):
|
"invoiced_amount", "payables", "bank_balance"):
|
||||||
if self.get(key):
|
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)
|
card = cache.get(cache_key)
|
||||||
|
|
||||||
if card:
|
if card:
|
||||||
@@ -289,6 +290,7 @@ class EmailDigest(Document):
|
|||||||
elif self.frequency == "Weekly":
|
elif self.frequency == "Weekly":
|
||||||
# from date is the previous week's monday
|
# from date is the previous week's monday
|
||||||
from_date = today - timedelta(days=today.weekday(), weeks=1)
|
from_date = today - timedelta(days=today.weekday(), weeks=1)
|
||||||
|
|
||||||
# to date is sunday i.e. the previous day
|
# to date is sunday i.e. the previous day
|
||||||
to_date = from_date + timedelta(days=6)
|
to_date = from_date + timedelta(days=6)
|
||||||
else:
|
else:
|
||||||
@@ -300,32 +302,18 @@ class EmailDigest(Document):
|
|||||||
return from_date, to_date
|
return from_date, to_date
|
||||||
|
|
||||||
def set_dates(self):
|
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
|
# decide from date based on email digest frequency
|
||||||
if self.frequency == "Daily":
|
if self.frequency == "Daily":
|
||||||
# from date, to_date is today
|
self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days = 1)
|
||||||
self.future_from_date = self.future_to_date = today
|
|
||||||
self.past_from_date = self.past_to_date = today - relativedelta(days = 1)
|
|
||||||
|
|
||||||
elif self.frequency == "Weekly":
|
elif self.frequency == "Weekly":
|
||||||
# from date is the current week's monday
|
self.past_from_date = self.future_from_date - relativedelta(weeks=1)
|
||||||
self.future_from_date = today - relativedelta(days=today.weekday())
|
self.past_to_date = self.future_from_date - relativedelta(days=1)
|
||||||
|
|
||||||
# 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)
|
|
||||||
else:
|
else:
|
||||||
# from date is the 1st day of the current month
|
self.past_from_date = self.future_from_date - relativedelta(months=1)
|
||||||
self.future_from_date = today - relativedelta(days=today.day-1)
|
self.past_to_date = self.future_from_date - relativedelta(days=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)
|
|
||||||
|
|
||||||
def get_next_sending(self):
|
def get_next_sending(self):
|
||||||
from_date, to_date = self.get_from_to_date()
|
from_date, to_date = self.get_from_to_date()
|
||||||
@@ -346,7 +334,7 @@ class EmailDigest(Document):
|
|||||||
self.get_next_sending()
|
self.get_next_sending()
|
||||||
|
|
||||||
def fmt_money(self, value):
|
def fmt_money(self, value):
|
||||||
return fmt_money(value, currency = self.currency)
|
return fmt_money(abs(value), currency = self.currency)
|
||||||
|
|
||||||
def send():
|
def send():
|
||||||
now_date = now_datetime().date()
|
now_date = now_datetime().date()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<div style="max-width: 500px; margin: auto; padding: 20px 0 40px 0">
|
<div style="max-width: 500px; margin: auto; padding: 20px 0 40px 0">
|
||||||
|
|
||||||
<h1 style="{{ h1 }}">{{ title }}</h1>
|
<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;">
|
<h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;">
|
||||||
<p>{% if frequency == "Daily" %}
|
<p>{% if frequency == "Daily" %}
|
||||||
{{ frappe.format_date(future_from_date) }}
|
{{ frappe.format_date(future_from_date) }}
|
||||||
@@ -52,6 +53,8 @@
|
|||||||
<span style="{{ label_css }}">
|
<span style="{{ label_css }}">
|
||||||
{% if e.all_day %}
|
{% if e.all_day %}
|
||||||
{{ _("All Day") }}
|
{{ _("All Day") }}
|
||||||
|
{% elif (not e.ends_on_label or e.starts_on_label == e.ends_on_label)%}
|
||||||
|
{{ e.starts_on_label }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ e.starts_on_label }} - {{ e.ends_on_label }}
|
{{ e.starts_on_label }} - {{ e.ends_on_label }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
import urllib
|
import urllib
|
||||||
|
from frappe.utils import nowdate
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
from frappe.website.render import clear_cache
|
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
|
concat(parent_website_route, "/", page_name) as route
|
||||||
from `tabItem`
|
from `tabItem`
|
||||||
where show_in_website = 1
|
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 (variant_of = '' or variant_of is null)
|
||||||
and (item_group in (%s)
|
and (item_group in ({child_groups})
|
||||||
or name in (select parent from `tabWebsite Item Group` where item_group in (%s)))
|
or name in (select parent from `tabWebsite Item Group` where item_group in ({child_groups})))
|
||||||
""" % (child_groups, child_groups)
|
""".format(child_groups=child_groups)
|
||||||
|
|
||||||
query += """order by weightage desc, modified desc limit %s, %s""" % (start, limit)
|
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]
|
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", {
|
return frappe.render_template("setup_wizard_message", {
|
||||||
image: "/assets/frappe/images/ui/bubble-tea-happy.svg",
|
image: "/assets/frappe/images/ui/bubble-tea-happy.svg",
|
||||||
title: __('Setup Complete'),
|
title: __('Setup Complete'),
|
||||||
message: __('Your setup is complete. Refreshing.') + ".."
|
message: ""
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -670,6 +670,7 @@ $.extend(erpnext.wiz, {
|
|||||||
args: values,
|
args: values,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
wiz.show_complete();
|
wiz.show_complete();
|
||||||
|
localStorage.setItem("session_last_route", "#welcome-to-erpnext");
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
window.location = "/desk";
|
window.location = "/desk";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|||||||
@@ -374,6 +374,7 @@ def create_items(args):
|
|||||||
is_sales_item = args.get("is_sales_item_" + str(i))
|
is_sales_item = args.get("is_sales_item_" + str(i))
|
||||||
is_purchase_item = args.get("is_purchase_item_" + str(i))
|
is_purchase_item = args.get("is_purchase_item_" + str(i))
|
||||||
is_stock_item = item_group!=_("Services")
|
is_stock_item = item_group!=_("Services")
|
||||||
|
is_pro_applicable = item_group!=_("Services")
|
||||||
default_warehouse = ""
|
default_warehouse = ""
|
||||||
if is_stock_item:
|
if is_stock_item:
|
||||||
default_warehouse = frappe.db.get_value("Warehouse", filters={
|
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,
|
"is_purchase_item": 1 if is_purchase_item else 0,
|
||||||
"show_in_website": 1,
|
"show_in_website": 1,
|
||||||
"is_stock_item": is_stock_item and 1 or 0,
|
"is_stock_item": is_stock_item and 1 or 0,
|
||||||
|
"is_pro_applicable": is_pro_applicable and 1 or 0,
|
||||||
"item_group": item_group,
|
"item_group": item_group,
|
||||||
"stock_uom": args.get("item_uom_" + str(i)),
|
"stock_uom": args.get("item_uom_" + str(i)),
|
||||||
"default_warehouse": default_warehouse
|
"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