mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-25 16:04:46 +00:00
Merge branch 'develop'
This commit is contained in:
@@ -16,7 +16,7 @@ ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a
|
|||||||
|
|
||||||
### Full Install
|
### Full Install
|
||||||
|
|
||||||
The Easy Way install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench
|
The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details.
|
||||||
|
|
||||||
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
|
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
|
||||||
|
|
||||||
@@ -71,6 +71,6 @@ We do not allow the use of the trademark in advertising, including AdSense/AdWor
|
|||||||
|
|
||||||
Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
|
Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
|
||||||
|
|
||||||
When in doubt about your use of the ERPNext name or logo, please contact the Frappe Technologies for clarification.
|
When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification.
|
||||||
|
|
||||||
(inspired from WordPress)
|
(inspired by WordPress)
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
__version__ = '5.5.1'
|
__version__ = '5.6.0'
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ class BankReconciliation(Document):
|
|||||||
|
|
||||||
|
|
||||||
dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit,
|
dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit,
|
||||||
t2.credit, t1.posting_date, t2.against_account, t1.clearance_date
|
t2.credit, t1.posting_date, t2.against_account, t1.clearance_date,
|
||||||
|
t2.reference_type, t2.reference_name
|
||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
{
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
"creation": "2013-02-22 01:27:37",
|
"creation": "2013-02-22 01:27:37",
|
||||||
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "voucher_id",
|
"fieldname": "voucher_id",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Voucher ID",
|
"label": "Voucher ID",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -13,46 +21,84 @@
|
|||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Journal Entry",
|
"options": "Journal Entry",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"search_index": 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,
|
||||||
"fieldname": "clearance_date",
|
"fieldname": "clearance_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Clearance Date",
|
"label": "Clearance Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "clearance_date",
|
"oldfieldname": "clearance_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"search_index": 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,
|
||||||
"fieldname": "against_account",
|
"fieldname": "against_account",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Against Account",
|
"label": "Against Account",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "against_account",
|
"oldfieldname": "against_account",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "cheque_number",
|
"fieldname": "cheque_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Cheque Number",
|
"label": "Cheque Number",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "cheque_number",
|
"oldfieldname": "cheque_number",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "debit",
|
"fieldname": "debit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Debit",
|
"label": "Debit",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -60,12 +106,21 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "credit",
|
"fieldname": "credit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Credit",
|
"label": "Credit",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -73,40 +128,113 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "reference_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Reference Type",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "DocType",
|
||||||
|
"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,
|
||||||
|
"fieldname": "reference_name",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Reference Name",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "reference_type",
|
||||||
|
"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,
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "posting_date",
|
"oldfieldname": "posting_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "cheque_date",
|
"fieldname": "cheque_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Cheque Date",
|
"label": "Cheque Date",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "cheque_date",
|
"oldfieldname": "cheque_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-04-21 01:29:29.570890",
|
"modified": "2015-08-10 16:59:43.974705",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Reconciliation Detail",
|
"name": "Bank Reconciliation Detail",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": []
|
"permissions": [],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0
|
||||||
}
|
}
|
||||||
@@ -50,32 +50,51 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
|
|
||||||
$.each([["against_voucher", "Purchase Invoice", "supplier"],
|
$.each([["against_voucher", "Purchase Invoice", "supplier"],
|
||||||
["against_invoice", "Sales Invoice", "customer"]], function(i, opts) {
|
["against_invoice", "Sales Invoice", "customer"]], function(i, opts) {
|
||||||
me.frm.set_query(opts[0], "accounts", function(doc, cdt, cdn) {
|
|
||||||
var jvd = frappe.get_doc(cdt, cdn);
|
|
||||||
frappe.model.validate_missing(jvd, "party_type");
|
|
||||||
frappe.model.validate_missing(jvd, "party");
|
|
||||||
return {
|
|
||||||
filters: [
|
|
||||||
[opts[1], opts[2], "=", jvd.party],
|
|
||||||
[opts[1], "docstatus", "=", 1],
|
|
||||||
[opts[1], "outstanding_amount", "!=", 0]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.frm.set_query("against_jv", "accounts", function(doc, cdt, cdn) {
|
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||||
var jvd = frappe.get_doc(cdt, cdn);
|
var jvd = frappe.get_doc(cdt, cdn);
|
||||||
frappe.model.validate_missing(jvd, "account");
|
|
||||||
|
|
||||||
return {
|
// expense claim
|
||||||
query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv",
|
if(jvd.reference_type==="Expense Claim") {
|
||||||
filters: {
|
return {};
|
||||||
account: jvd.account,
|
}
|
||||||
party: jvd.party
|
|
||||||
}
|
// journal entry
|
||||||
|
if(jvd.reference_type==="Journal Entry") {
|
||||||
|
frappe.model.validate_missing(jvd, "account");
|
||||||
|
|
||||||
|
return {
|
||||||
|
query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv",
|
||||||
|
filters: {
|
||||||
|
account: jvd.account,
|
||||||
|
party: jvd.party
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// against party
|
||||||
|
|
||||||
|
frappe.model.validate_missing(jvd, "party_type");
|
||||||
|
frappe.model.validate_missing(jvd, "party");
|
||||||
|
|
||||||
|
var out = {
|
||||||
|
filters: [
|
||||||
|
[jvd.reference_type, jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier", "=", jvd.party],
|
||||||
|
[jvd.reference_type, "docstatus", "=", 1],
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) {
|
||||||
|
out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]);
|
||||||
|
} else {
|
||||||
|
out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_balance_formatter: function() {
|
setup_balance_formatter: function() {
|
||||||
@@ -93,24 +112,16 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
against_voucher: function(doc, cdt, cdn) {
|
reference_name: function(doc, cdt, cdn) {
|
||||||
var d = frappe.get_doc(cdt, cdn);
|
var d = frappe.get_doc(cdt, cdn);
|
||||||
if (d.against_voucher && !flt(d.debit)) {
|
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
|
||||||
this.get_outstanding('Purchase Invoice', d.against_voucher, d);
|
this.get_outstanding('Purchase Invoice', d.reference_name, d);
|
||||||
}
|
}
|
||||||
},
|
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
|
||||||
|
this.get_outstanding('Sales Invoice', d.reference_name, d);
|
||||||
against_invoice: function(doc, cdt, cdn) {
|
|
||||||
var d = frappe.get_doc(cdt, cdn);
|
|
||||||
if (d.against_invoice && !flt(d.credit)) {
|
|
||||||
this.get_outstanding('Sales Invoice', d.against_invoice, d);
|
|
||||||
}
|
}
|
||||||
},
|
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
|
||||||
|
this.get_outstanding('Journal Entry', d.reference_name, d);
|
||||||
against_jv: function(doc, cdt, cdn) {
|
|
||||||
var d = frappe.get_doc(cdt, cdn);
|
|
||||||
if (d.against_jv && !flt(d.credit) && !flt(d.debit)) {
|
|
||||||
this.get_outstanding('Journal Entry', d.against_jv, d);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -214,11 +225,12 @@ cur_frm.cscript.account = function(doc,dt,dn) {
|
|||||||
var d = locals[dt][dn];
|
var d = locals[dt][dn];
|
||||||
if(d.account) {
|
if(d.account) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.accounts.utils.get_balance_on",
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
|
||||||
args: {account: d.account, date: doc.posting_date},
|
args: {account: d.account, date: doc.posting_date},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
d.balance = r.message;
|
$.extend(d, r.message);
|
||||||
refresh_field('balance', d.name, 'accounts');
|
refresh_field('balance', d.name, 'accounts');
|
||||||
|
refresh_field('party_type', d.name, 'accounts');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,10 @@ class JournalEntry(AccountsController):
|
|||||||
self.validate_entries_for_advance()
|
self.validate_entries_for_advance()
|
||||||
self.validate_debit_and_credit()
|
self.validate_debit_and_credit()
|
||||||
self.validate_against_jv()
|
self.validate_against_jv()
|
||||||
self.validate_against_sales_invoice()
|
self.validate_reference_doc()
|
||||||
self.validate_against_purchase_invoice()
|
|
||||||
self.set_against_account()
|
self.set_against_account()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
self.validate_against_sales_order()
|
|
||||||
self.validate_against_purchase_order()
|
|
||||||
self.check_due_date()
|
self.check_due_date()
|
||||||
self.validate_expense_claim()
|
self.validate_expense_claim()
|
||||||
self.validate_credit_debit_note()
|
self.validate_credit_debit_note()
|
||||||
@@ -54,10 +51,8 @@ class JournalEntry(AccountsController):
|
|||||||
advance_paid = frappe._dict()
|
advance_paid = frappe._dict()
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.is_advance:
|
if d.is_advance:
|
||||||
if d.against_sales_order:
|
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||||
advance_paid.setdefault("Sales Order", []).append(d.against_sales_order)
|
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||||
elif d.against_purchase_order:
|
|
||||||
advance_paid.setdefault("Purchase Order", []).append(d.against_purchase_order)
|
|
||||||
|
|
||||||
for voucher_type, order_list in advance_paid.items():
|
for voucher_type, order_list in advance_paid.items():
|
||||||
for voucher_no in list(set(order_list)):
|
for voucher_no in list(set(order_list)):
|
||||||
@@ -65,7 +60,7 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
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, "against_jv")
|
remove_against_link_from_jv(self.doctype, self.name)
|
||||||
|
|
||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
@@ -93,10 +88,8 @@ class JournalEntry(AccountsController):
|
|||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.party_type and d.party and d.get("credit" if d.party_type=="Customer" else "debit") > 0:
|
if d.party_type and d.party and d.get("credit" if d.party_type=="Customer" else "debit") > 0:
|
||||||
due_date = None
|
due_date = None
|
||||||
if d.against_invoice:
|
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||||
due_date = frappe.db.get_value("Sales Invoice", d.against_invoice, "due_date")
|
due_date = frappe.db.get_value(d.reference_type, d.reference_name, "due_date")
|
||||||
elif d.against_voucher:
|
|
||||||
due_date = frappe.db.get_value("Purchase Invoice", d.against_voucher, "due_date")
|
|
||||||
|
|
||||||
if due_date and getdate(self.cheque_date) > getdate(due_date):
|
if due_date and getdate(self.cheque_date) > getdate(due_date):
|
||||||
diff = date_diff(self.cheque_date, due_date)
|
diff = date_diff(self.cheque_date, due_date)
|
||||||
@@ -115,17 +108,17 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
def validate_entries_for_advance(self):
|
def validate_entries_for_advance(self):
|
||||||
for d in self.get('accounts'):
|
for d in self.get('accounts'):
|
||||||
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
if d.reference_type not in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
|
||||||
if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
|
if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
|
||||||
(d.party_type == 'Supplier' and flt(d.debit) > 0):
|
(d.party_type == 'Supplier' and flt(d.debit) > 0):
|
||||||
if not d.is_advance:
|
if d.is_advance=="No":
|
||||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||||
elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes":
|
elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
|
||||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||||
|
|
||||||
def validate_against_jv(self):
|
def validate_against_jv(self):
|
||||||
for d in self.get('accounts'):
|
for d in self.get('accounts'):
|
||||||
if d.against_jv:
|
if d.reference_type=="Journal Entry":
|
||||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||||
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
|
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
|
||||||
@@ -134,17 +127,17 @@ class JournalEntry(AccountsController):
|
|||||||
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
|
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
|
||||||
.format(d.account))
|
.format(d.account))
|
||||||
|
|
||||||
if d.against_jv == self.name:
|
if d.reference_name == self.name:
|
||||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
|
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
|
||||||
|
|
||||||
against_entries = frappe.db.sql("""select * from `tabJournal Entry Account`
|
against_entries = frappe.db.sql("""select * from `tabJournal Entry Account`
|
||||||
where account = %s and docstatus = 1 and parent = %s
|
where account = %s and docstatus = 1 and parent = %s
|
||||||
and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = ''
|
and ifnull(reference_type, '') in ("", "Sales Order", "Purchase Order")
|
||||||
and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
""", (d.account, d.reference_name), as_dict=True)
|
||||||
|
|
||||||
if not against_entries:
|
if not against_entries:
|
||||||
frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher")
|
frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher")
|
||||||
.format(d.against_jv, d.account))
|
.format(d.reference_name, d.account))
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||||
valid = False
|
valid = False
|
||||||
@@ -153,88 +146,99 @@ class JournalEntry(AccountsController):
|
|||||||
valid = True
|
valid = True
|
||||||
if not valid:
|
if not valid:
|
||||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||||
.format(d.against_jv, dr_or_cr))
|
.format(d.reference_name, dr_or_cr))
|
||||||
|
|
||||||
def validate_against_sales_invoice(self):
|
def validate_reference_doc(self):
|
||||||
self.validate_account_in_against_voucher("against_invoice", "Sales Invoice")
|
"""Validates reference document"""
|
||||||
|
field_dict = {
|
||||||
def validate_against_purchase_invoice(self):
|
'Sales Invoice': ["Customer", "Debit To"],
|
||||||
self.validate_account_in_against_voucher("against_voucher", "Purchase Invoice")
|
|
||||||
|
|
||||||
def validate_against_sales_order(self):
|
|
||||||
payment_against_voucher = self.validate_account_in_against_voucher("against_sales_order", "Sales Order")
|
|
||||||
self.validate_against_order_fields("Sales Order", payment_against_voucher)
|
|
||||||
|
|
||||||
def validate_against_purchase_order(self):
|
|
||||||
payment_against_voucher = self.validate_account_in_against_voucher("against_purchase_order", "Purchase Order")
|
|
||||||
self.validate_against_order_fields("Purchase Order", payment_against_voucher)
|
|
||||||
|
|
||||||
def validate_account_in_against_voucher(self, against_field, doctype):
|
|
||||||
payment_against_voucher = frappe._dict()
|
|
||||||
field_dict = {'Sales Invoice': ["Customer", "Debit To"],
|
|
||||||
'Purchase Invoice': ["Supplier", "Credit To"],
|
'Purchase Invoice': ["Supplier", "Credit To"],
|
||||||
'Sales Order': ["Customer"],
|
'Sales Order': ["Customer"],
|
||||||
'Purchase Order': ["Supplier"]
|
'Purchase Order': ["Supplier"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.reference_totals = {}
|
||||||
|
self.reference_types = {}
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.get(against_field):
|
if not d.reference_type:
|
||||||
dr_or_cr = "credit" if against_field in ["against_invoice", "against_sales_order"] \
|
d.reference_name = None
|
||||||
|
if not d.reference_name:
|
||||||
|
d.reference_type = None
|
||||||
|
if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()):
|
||||||
|
dr_or_cr = "credit" if d.reference_type in ("Sales Order", "Sales Invoice") \
|
||||||
else "debit"
|
else "debit"
|
||||||
if against_field == "against_sales_order" and flt(d.debit) > 0:
|
|
||||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, doctype))
|
|
||||||
|
|
||||||
if against_field == "against_purchase_order" and flt(d.credit) > 0:
|
# check debit or credit type Sales / Purchase Order
|
||||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, doctype))
|
if d.reference_type=="Sales Order" and flt(d.debit) > 0:
|
||||||
|
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type))
|
||||||
|
|
||||||
against_voucher = frappe.db.get_value(doctype, d.get(against_field),
|
if d.reference_type == "Purchase Order" and flt(d.credit) > 0:
|
||||||
[scrub(dt) for dt in field_dict.get(doctype)])
|
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type))
|
||||||
|
|
||||||
if against_field in ["against_invoice", "against_voucher"]:
|
# set totals
|
||||||
if (against_voucher[0] !=d.party or against_voucher[1] != d.account):
|
if not d.reference_name in self.reference_totals:
|
||||||
frappe.throw(_("Row {0}: Party / Account does not match with \
|
self.reference_totals[d.reference_name] = 0.0
|
||||||
Customer / Debit To in {1}").format(d.idx, doctype))
|
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
|
||||||
else:
|
self.reference_types[d.reference_name] = d.reference_type
|
||||||
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
|
||||||
|
|
||||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
||||||
|
[scrub(dt) for dt in field_dict.get(d.reference_type)])
|
||||||
|
|
||||||
|
# check if party and account match
|
||||||
|
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
|
||||||
|
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
|
||||||
|
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
||||||
|
d.reference_type, d.reference_name))
|
||||||
|
|
||||||
|
# check if party matches for Sales / Purchase Order
|
||||||
|
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||||
|
# set totals
|
||||||
if against_voucher != d.party:
|
if against_voucher != d.party:
|
||||||
frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
|
frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
|
||||||
.format(d.idx, d.party_type, d.party, doctype))
|
.format(d.idx, d.party_type, d.party, d.reference_type))
|
||||||
elif d.is_advance == "Yes":
|
|
||||||
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
|
||||||
|
|
||||||
return payment_against_voucher
|
self.validate_orders()
|
||||||
|
self.validate_invoices()
|
||||||
|
|
||||||
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
def validate_orders(self):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
"""Validate totals, stopped and docstatus for orders"""
|
||||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
for reference_name, total in self.reference_totals.iteritems():
|
||||||
["docstatus", "outstanding_amount"])
|
reference_type = self.reference_types[reference_name]
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if reference_type in ("Sales Order", "Purchase Order"):
|
||||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
voucher_properties = frappe.db.get_value(reference_type, reference_name,
|
||||||
|
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"])
|
||||||
|
|
||||||
if flt(voucher_properties[1]) < flt(sum(payment_list)):
|
if voucher_properties[0] != 1:
|
||||||
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
|
||||||
than Outstanding Amount {2}").format(doctype, voucher_no, voucher_properties[1]))
|
|
||||||
|
|
||||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
if flt(voucher_properties[1]) >= 100:
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name))
|
||||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
|
||||||
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"])
|
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if cstr(voucher_properties[2]) == "Stopped":
|
||||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
|
||||||
|
|
||||||
if flt(voucher_properties[1]) >= 100:
|
if flt(voucher_properties[4]) < (flt(voucher_properties[3]) + total):
|
||||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||||
|
than Grand Total {2}").format(reference_type, reference_name, voucher_properties[4]))
|
||||||
|
|
||||||
if cstr(voucher_properties[2]) == "Stopped":
|
def validate_invoices(self):
|
||||||
frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no))
|
"""Validate totals and docstatus for invoices"""
|
||||||
|
for reference_name, total in self.reference_totals.iteritems():
|
||||||
|
reference_type = self.reference_types[reference_name]
|
||||||
|
|
||||||
if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)):
|
if reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||||
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
voucher_properties = frappe.db.get_value(reference_type, reference_name,
|
||||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
["docstatus", "outstanding_amount"])
|
||||||
|
|
||||||
|
if voucher_properties[0] != 1:
|
||||||
|
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
|
||||||
|
|
||||||
|
if flt(voucher_properties[1]) < total:
|
||||||
|
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
||||||
|
than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1]))
|
||||||
|
|
||||||
def set_against_account(self):
|
def set_against_account(self):
|
||||||
accounts_debited, accounts_credited = [], []
|
accounts_debited, accounts_credited = [], []
|
||||||
@@ -274,25 +278,25 @@ class JournalEntry(AccountsController):
|
|||||||
company_currency = get_company_currency(self.company)
|
company_currency = get_company_currency(self.company)
|
||||||
|
|
||||||
for d in self.get('accounts'):
|
for d in self.get('accounts'):
|
||||||
if d.against_invoice and d.credit:
|
if d.reference_type=="Sales Invoice" and d.credit:
|
||||||
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||||
d.against_invoice))
|
d.reference_name))
|
||||||
|
|
||||||
if d.against_sales_order and d.credit:
|
if d.reference_type=="Sales Order" and d.credit:
|
||||||
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||||
d.against_sales_order))
|
d.reference_name))
|
||||||
|
|
||||||
if d.against_voucher and d.debit:
|
if d.reference_type == "Purchase Invoice" and d.debit:
|
||||||
bill_no = frappe.db.sql("""select bill_no, bill_date
|
bill_no = frappe.db.sql("""select bill_no, bill_date
|
||||||
from `tabPurchase Invoice` where name=%s""", d.against_voucher)
|
from `tabPurchase Invoice` where name=%s""", d.reference_name)
|
||||||
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
|
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
|
||||||
not in ['na', 'not applicable', 'none']:
|
not in ['na', 'not applicable', 'none']:
|
||||||
r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0],
|
r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0],
|
||||||
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
||||||
|
|
||||||
if d.against_purchase_order and d.debit:
|
if d.reference_type == "Purchase Order" and d.debit:
|
||||||
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||||
d.against_purchase_order))
|
d.reference_name))
|
||||||
|
|
||||||
if self.user_remark:
|
if self.user_remark:
|
||||||
r.append(_("Note: {0}").format(self.user_remark))
|
r.append(_("Note: {0}").format(self.user_remark))
|
||||||
@@ -331,13 +335,8 @@ class JournalEntry(AccountsController):
|
|||||||
"against": d.against_account,
|
"against": d.against_account,
|
||||||
"debit": flt(d.debit, self.precision("debit", "accounts")),
|
"debit": flt(d.debit, self.precision("debit", "accounts")),
|
||||||
"credit": flt(d.credit, self.precision("credit", "accounts")),
|
"credit": flt(d.credit, self.precision("credit", "accounts")),
|
||||||
"against_voucher_type": (("Purchase Invoice" if d.against_voucher else None)
|
"against_voucher_type": d.reference_type,
|
||||||
or ("Sales Invoice" if d.against_invoice else None)
|
"against_voucher": d.reference_name,
|
||||||
or ("Journal Entry" if d.against_jv else None)
|
|
||||||
or ("Sales Order" if d.against_sales_order else None)
|
|
||||||
or ("Purchase Order" if d.against_purchase_order else None)),
|
|
||||||
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv
|
|
||||||
or d.against_sales_order or d.against_purchase_order,
|
|
||||||
"remarks": self.remark,
|
"remarks": self.remark,
|
||||||
"cost_center": d.cost_center
|
"cost_center": d.cost_center
|
||||||
})
|
})
|
||||||
@@ -384,11 +383,13 @@ class JournalEntry(AccountsController):
|
|||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
jd1.party_type = "Customer"
|
jd1.party_type = "Customer"
|
||||||
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||||
jd1.against_invoice = cstr(d.name)
|
jd1.reference_type = "Sales Invoice"
|
||||||
|
jd1.reference_name = cstr(d.name)
|
||||||
elif self.write_off_based_on == 'Accounts Payable':
|
elif self.write_off_based_on == 'Accounts Payable':
|
||||||
jd1.party_type = "Supplier"
|
jd1.party_type = "Supplier"
|
||||||
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||||
jd1.against_voucher = cstr(d.name)
|
jd1.reference_type = "Purchase Invoice"
|
||||||
|
jd1.reference_name = cstr(d.name)
|
||||||
|
|
||||||
jd2 = self.append('accounts', {})
|
jd2 = self.append('accounts', {})
|
||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
@@ -414,19 +415,20 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
def update_expense_claim(self):
|
def update_expense_claim(self):
|
||||||
for d in self.accounts:
|
for d in self.accounts:
|
||||||
if d.against_expense_claim:
|
if d.reference_type=="Expense Claim":
|
||||||
amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account`
|
amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account`
|
||||||
where against_expense_claim = %s and docstatus = 1""", d.against_expense_claim ,as_dict=1)[0].amt
|
where reference_type = "Expense Claim" and
|
||||||
frappe.db.set_value("Expense Claim", d.against_expense_claim , "total_amount_reimbursed", amt)
|
reference_name = %s and docstatus = 1""", d.reference_name ,as_dict=1)[0].amt
|
||||||
|
frappe.db.set_value("Expense Claim", d.reference_name , "total_amount_reimbursed", amt)
|
||||||
|
|
||||||
def validate_expense_claim(self):
|
def validate_expense_claim(self):
|
||||||
for d in self.accounts:
|
for d in self.accounts:
|
||||||
if d.against_expense_claim:
|
if d.reference_type=="Expense Claim":
|
||||||
sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim",
|
sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim",
|
||||||
d.against_expense_claim, ("total_sanctioned_amount", "total_amount_reimbursed"))
|
d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed"))
|
||||||
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
|
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
|
||||||
if d.debit > pending_amount:
|
if d.debit > pending_amount:
|
||||||
frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.against_expense_claim, pending_amount)))
|
frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.reference_name, pending_amount)))
|
||||||
|
|
||||||
def validate_credit_debit_note(self):
|
def validate_credit_debit_note(self):
|
||||||
if self.stock_entry:
|
if self.stock_entry:
|
||||||
@@ -466,6 +468,7 @@ 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_from_sales_invoice(sales_invoice):
|
||||||
|
"""Returns new Journal Entry document as dict for given Sales Invoice"""
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
si = frappe.get_doc("Sales Invoice", sales_invoice)
|
si = frappe.get_doc("Sales Invoice", sales_invoice)
|
||||||
jv = get_payment_entry(si)
|
jv = get_payment_entry(si)
|
||||||
@@ -478,7 +481,8 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
|
|||||||
jv.get("accounts")[0].balance = get_balance_on(si.debit_to)
|
jv.get("accounts")[0].balance = get_balance_on(si.debit_to)
|
||||||
jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer")
|
jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer")
|
||||||
jv.get("accounts")[0].credit = si.outstanding_amount
|
jv.get("accounts")[0].credit = si.outstanding_amount
|
||||||
jv.get("accounts")[0].against_invoice = si.name
|
jv.get("accounts")[0].reference_type = si.doctype
|
||||||
|
jv.get("accounts")[0].reference_name = si.name
|
||||||
|
|
||||||
# debit bank
|
# debit bank
|
||||||
jv.get("accounts")[1].debit = si.outstanding_amount
|
jv.get("accounts")[1].debit = si.outstanding_amount
|
||||||
@@ -487,6 +491,7 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry_from_purchase_invoice(purchase_invoice):
|
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)
|
pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
|
||||||
jv = get_payment_entry(pi)
|
jv = get_payment_entry(pi)
|
||||||
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
|
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
|
||||||
@@ -498,13 +503,78 @@ def get_payment_entry_from_purchase_invoice(purchase_invoice):
|
|||||||
jv.get("accounts")[0].balance = get_balance_on(pi.credit_to)
|
jv.get("accounts")[0].balance = get_balance_on(pi.credit_to)
|
||||||
jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
|
jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
|
||||||
jv.get("accounts")[0].debit = pi.outstanding_amount
|
jv.get("accounts")[0].debit = pi.outstanding_amount
|
||||||
jv.get("accounts")[0].against_voucher = pi.name
|
jv.get("accounts")[0].reference_type = pi.doctype
|
||||||
|
jv.get("accounts")[0].reference_name = pi.name
|
||||||
|
|
||||||
# credit bank
|
# credit bank
|
||||||
jv.get("accounts")[1].credit = pi.outstanding_amount
|
jv.get("accounts")[1].credit = pi.outstanding_amount
|
||||||
|
|
||||||
return jv.as_dict()
|
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(so.company, so.customer, "Customer")
|
||||||
|
|
||||||
|
amount = flt(so.base_grand_total) - flt(so.advance_paid)
|
||||||
|
|
||||||
|
# credit customer
|
||||||
|
jv.get("accounts")[0].account = party_account
|
||||||
|
jv.get("accounts")[0].party_type = "Customer"
|
||||||
|
jv.get("accounts")[0].party = so.customer
|
||||||
|
jv.get("accounts")[0].balance = get_balance_on(party_account)
|
||||||
|
jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer")
|
||||||
|
jv.get("accounts")[0].credit = amount
|
||||||
|
jv.get("accounts")[0].reference_type = so.doctype
|
||||||
|
jv.get("accounts")[0].reference_name = so.name
|
||||||
|
jv.get("accounts")[0].is_advance = "Yes"
|
||||||
|
|
||||||
|
# debit bank
|
||||||
|
jv.get("accounts")[1].debit = amount
|
||||||
|
|
||||||
|
return jv.as_dict()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_payment_entry_from_purchase_order(purchase_order):
|
||||||
|
"""Returns new Journal Entry document as dict for given Sales Order"""
|
||||||
|
from erpnext.accounts.utils import get_balance_on
|
||||||
|
from erpnext.accounts.party import get_party_account
|
||||||
|
po = frappe.get_doc("Purchase Order", purchase_order)
|
||||||
|
|
||||||
|
if flt(po.per_billed, 2) != 0.0:
|
||||||
|
frappe.throw(_("Can only make payment against unbilled Sales Order"))
|
||||||
|
|
||||||
|
jv = get_payment_entry(po)
|
||||||
|
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
|
||||||
|
party_account = get_party_account(po.company, po.supplier, "Supplier")
|
||||||
|
|
||||||
|
amount = flt(po.base_grand_total) - flt(po.advance_paid)
|
||||||
|
|
||||||
|
# credit customer
|
||||||
|
jv.get("accounts")[0].account = party_account
|
||||||
|
jv.get("accounts")[0].party_type = "Supplier"
|
||||||
|
jv.get("accounts")[0].party = po.supplier
|
||||||
|
jv.get("accounts")[0].balance = get_balance_on(party_account)
|
||||||
|
jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
|
||||||
|
jv.get("accounts")[0].debit = amount
|
||||||
|
jv.get("accounts")[0].reference_type = po.doctype
|
||||||
|
jv.get("accounts")[0].reference_name = po.name
|
||||||
|
jv.get("accounts")[0].is_advance = "Yes"
|
||||||
|
|
||||||
|
# debit bank
|
||||||
|
jv.get("accounts")[1].credit = amount
|
||||||
|
|
||||||
|
return jv.as_dict()
|
||||||
|
|
||||||
def get_payment_entry(doc):
|
def get_payment_entry(doc):
|
||||||
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
|
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
|
||||||
|
|
||||||
@@ -535,13 +605,14 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
||||||
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||||
where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s
|
where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s
|
||||||
and (ifnull(jv_detail.against_invoice, '') = '' and ifnull(jv_detail.against_voucher, '') = ''
|
and (ifnull(jv_detail.reference_type, '') = ''
|
||||||
and ifnull(jv_detail.against_jv, '') = '' )
|
|
||||||
and jv.docstatus = 1 and jv.{0} like %s order by jv.name desc limit %s, %s""".format(searchfield),
|
and jv.docstatus = 1 and jv.{0} like %s order by jv.name desc limit %s, %s""".format(searchfield),
|
||||||
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
|
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding(args):
|
def get_outstanding(args):
|
||||||
|
if not frappe.has_permission("Account"):
|
||||||
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
args = eval(args)
|
args = eval(args)
|
||||||
if args.get("doctype") == "Journal Entry":
|
if args.get("doctype") == "Journal Entry":
|
||||||
condition = " and party=%(party)s" if args.get("party") else ""
|
condition = " and party=%(party)s" if args.get("party") else ""
|
||||||
@@ -549,8 +620,7 @@ def get_outstanding(args):
|
|||||||
against_jv_amount = frappe.db.sql("""
|
against_jv_amount = frappe.db.sql("""
|
||||||
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
|
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
|
||||||
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
|
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
|
||||||
and ifnull(against_invoice, '')='' and ifnull(against_voucher, '')=''
|
and ifnull(reference_type, '')=''""".format(condition), args)
|
||||||
and ifnull(against_jv, '')=''""".format(condition), args)
|
|
||||||
|
|
||||||
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
|
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
|
||||||
return {
|
return {
|
||||||
@@ -569,6 +639,9 @@ def get_outstanding(args):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_account_and_balance(company, party_type, party):
|
def get_party_account_and_balance(company, party_type, party):
|
||||||
|
if not frappe.has_permission("Account"):
|
||||||
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
account = get_party_account(company, party, party_type)
|
account = get_party_account(company, party, party_type)
|
||||||
|
|
||||||
@@ -580,3 +653,16 @@ def get_party_account_and_balance(company, party_type, party):
|
|||||||
"balance": account_balance,
|
"balance": account_balance,
|
||||||
"party_balance": party_balance
|
"party_balance": party_balance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_account_balance_and_party_type(account, date):
|
||||||
|
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
|
||||||
|
if not frappe.has_permission("Account"):
|
||||||
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
|
account_type = frappe.db.get_value("Account", account, "account_type")
|
||||||
|
return {
|
||||||
|
"balance": get_balance_on(account, date),
|
||||||
|
"party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
|
|
||||||
def jv_against_voucher_testcase(self, base_jv, test_voucher):
|
def jv_against_voucher_testcase(self, base_jv, test_voucher):
|
||||||
dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Entry"] else "debit"
|
dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Entry"] else "debit"
|
||||||
field_dict = {'Journal Entry': "against_jv",
|
|
||||||
'Sales Order': "against_sales_order",
|
|
||||||
'Purchase Order': "against_purchase_order"
|
|
||||||
}
|
|
||||||
|
|
||||||
test_voucher.insert()
|
test_voucher.insert()
|
||||||
test_voucher.submit()
|
test_voucher.submit()
|
||||||
@@ -42,21 +38,20 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
where account = %s and docstatus = 1 and parent = %s""",
|
where account = %s and docstatus = 1 and parent = %s""",
|
||||||
("_Test Receivable - _TC", test_voucher.name)))
|
("_Test Receivable - _TC", test_voucher.name)))
|
||||||
|
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (test_voucher.name)))
|
where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name)))
|
||||||
|
|
||||||
base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
|
base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
|
||||||
base_jv.get("accounts")[0].set(field_dict.get(test_voucher.doctype), test_voucher.name)
|
base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype)
|
||||||
|
base_jv.get("accounts")[0].set("reference_name", test_voucher.name)
|
||||||
base_jv.insert()
|
base_jv.insert()
|
||||||
base_jv.submit()
|
base_jv.submit()
|
||||||
|
|
||||||
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (submitted_voucher.name)))
|
where reference_type = %s and reference_name = %s and {0}=400""".format(dr_or_cr),
|
||||||
|
(submitted_voucher.doctype, submitted_voucher.name)))
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
|
||||||
where %s=%s and %s=400""" % (field_dict.get(submitted_voucher.doctype), '%s', dr_or_cr), (submitted_voucher.name)))
|
|
||||||
|
|
||||||
if base_jv.get("accounts")[0].is_advance == "Yes":
|
if base_jv.get("accounts")[0].is_advance == "Yes":
|
||||||
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
|
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
|
||||||
@@ -74,8 +69,8 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
if test_voucher.doctype == "Journal Entry":
|
if test_voucher.doctype == "Journal Entry":
|
||||||
# if test_voucher is a Journal Entry, test cancellation of test_voucher
|
# if test_voucher is a Journal Entry, test cancellation of test_voucher
|
||||||
test_voucher.cancel()
|
test_voucher.cancel()
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_jv=%s""", test_voucher.name))
|
where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name))
|
||||||
|
|
||||||
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
|
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
|
||||||
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
|
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
|
||||||
|
|||||||
@@ -1,27 +1,44 @@
|
|||||||
{
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-02-22 01:27:39",
|
"creation": "2013-02-22 01:27:39",
|
||||||
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "account",
|
"oldfieldname": "account",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"print_width": "250px",
|
"print_width": "250px",
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "250px"
|
"width": "250px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "balance",
|
"fieldname": "balance",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Account Balance",
|
"label": "Account Balance",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -30,186 +47,336 @@
|
|||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"default": ":Company",
|
"default": ":Company",
|
||||||
"description": "If Income or Expense",
|
"description": "If Income or Expense",
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Cost Center",
|
"label": "Cost Center",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "cost_center",
|
"oldfieldname": "cost_center",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "180px",
|
"print_width": "180px",
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "180px"
|
"width": "180px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "party_type",
|
"fieldname": "party_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Party Type",
|
"label": "Party Type",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "party",
|
"fieldname": "party",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Party",
|
"label": "Party",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "party_type",
|
"options": "party_type",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "party_balance",
|
"fieldname": "party_balance",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Party Balance",
|
"label": "Party Balance",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "sec_break1",
|
"fieldname": "sec_break1",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"permlevel": 0
|
"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,
|
||||||
"fieldname": "debit",
|
"fieldname": "debit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Debit",
|
"label": "Debit",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "debit",
|
"oldfieldname": "debit",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "col_break2",
|
"fieldname": "col_break2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "credit",
|
"fieldname": "credit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Credit",
|
"label": "Credit",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "credit",
|
"oldfieldname": "credit",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "reference",
|
"fieldname": "reference",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Reference",
|
"label": "Reference",
|
||||||
"permlevel": 0
|
"no_copy": 0,
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "against_invoice",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_filter": 1,
|
|
||||||
"label": "Against Sales Invoice",
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "against_invoice",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Sales Invoice",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"search_index": 1
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "against_voucher",
|
"allow_on_submit": 0,
|
||||||
"fieldtype": "Link",
|
"fieldname": "reference_type",
|
||||||
"in_filter": 1,
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"hidden": 0,
|
||||||
"label": "Against Purchase Invoice",
|
"ignore_user_permissions": 0,
|
||||||
"no_copy": 1,
|
"in_filter": 0,
|
||||||
"oldfieldname": "against_voucher",
|
"in_list_view": 0,
|
||||||
"oldfieldtype": "Link",
|
"label": "Reference Type",
|
||||||
"options": "Purchase Invoice",
|
"no_copy": 0,
|
||||||
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"search_index": 1
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "against_jv",
|
"allow_on_submit": 0,
|
||||||
"fieldtype": "Link",
|
"fieldname": "reference_name",
|
||||||
"in_filter": 1,
|
"fieldtype": "Dynamic Link",
|
||||||
"label": "Against Journal Entry",
|
"hidden": 0,
|
||||||
"no_copy": 1,
|
"ignore_user_permissions": 0,
|
||||||
"oldfieldname": "against_jv",
|
"in_filter": 0,
|
||||||
"oldfieldtype": "Link",
|
"in_list_view": 0,
|
||||||
"options": "Journal Entry",
|
"label": "Reference Name",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "reference_type",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"search_index": 1
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break3",
|
"fieldname": "col_break3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
},
|
"ignore_user_permissions": 0,
|
||||||
{
|
"in_filter": 0,
|
||||||
"fieldname": "against_sales_order",
|
"in_list_view": 0,
|
||||||
"fieldtype": "Link",
|
"no_copy": 0,
|
||||||
"label": "Against Sales Order",
|
|
||||||
"options": "Sales Order",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "against_purchase_order",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Against Purchase Order",
|
|
||||||
"options": "Purchase Order",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "against_expense_claim",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Against Expense Claim",
|
|
||||||
"options": "Expense Claim",
|
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "is_advance",
|
"fieldname": "is_advance",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Is Advance",
|
"label": "Is Advance",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "is_advance",
|
"oldfieldname": "is_advance",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "No\nYes",
|
"options": "No\nYes",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "against_account",
|
"fieldname": "against_account",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Against Account",
|
"label": "Against Account",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "against_account",
|
"oldfieldname": "against_account",
|
||||||
"oldfieldtype": "Text",
|
"oldfieldtype": "Text",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-02-19 01:07:00.388689",
|
"modified": "2015-08-11 10:44:11.432623",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry Account",
|
"name": "Journal Entry Account",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": []
|
"permissions": [],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0
|
||||||
}
|
}
|
||||||
@@ -34,8 +34,8 @@ class PaymentReconciliation(Document):
|
|||||||
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
||||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||||
and t2.account = %(account)s and {dr_or_cr} > 0
|
and t2.account = %(account)s and {dr_or_cr} > 0
|
||||||
and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
|
and ifnull(t2.reference_type, '') in ('', 'Sales Order', 'Purchase Order')
|
||||||
and ifnull(t2.against_jv, '')='' {cond}
|
{cond}
|
||||||
and (CASE
|
and (CASE
|
||||||
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
|
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
|
||||||
THEN 1=1
|
THEN 1=1
|
||||||
|
|||||||
@@ -12,13 +12,6 @@ class PaymentTool(Document):
|
|||||||
def make_journal_entry(self):
|
def make_journal_entry(self):
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
total_payment_amount = 0.00
|
total_payment_amount = 0.00
|
||||||
invoice_voucher_type = {
|
|
||||||
'Sales Invoice': 'against_invoice',
|
|
||||||
'Purchase Invoice': 'against_voucher',
|
|
||||||
'Journal Entry': 'against_jv',
|
|
||||||
'Sales Order': 'against_sales_order',
|
|
||||||
'Purchase Order': 'against_purchase_order',
|
|
||||||
}
|
|
||||||
|
|
||||||
jv = frappe.new_doc('Journal Entry')
|
jv = frappe.new_doc('Journal Entry')
|
||||||
jv.voucher_type = 'Journal Entry'
|
jv.voucher_type = 'Journal Entry'
|
||||||
@@ -41,7 +34,8 @@ class PaymentTool(Document):
|
|||||||
d1.party = self.party
|
d1.party = self.party
|
||||||
d1.balance = get_balance_on(self.party_account)
|
d1.balance = get_balance_on(self.party_account)
|
||||||
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount))
|
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount))
|
||||||
d1.set(invoice_voucher_type.get(v.against_voucher_type), v.against_voucher_no)
|
d1.set("reference_type", v.against_voucher_type)
|
||||||
|
d1.set("reference_name", v.against_voucher_no)
|
||||||
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
|
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
|
||||||
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit)
|
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
self.create_against_jv(jv_test_records[0], {
|
self.create_against_jv(jv_test_records[0], {
|
||||||
"party": "_Test Customer 3",
|
"party": "_Test Customer 3",
|
||||||
"against_sales_order": so1.name,
|
"reference_type": "Sales Order",
|
||||||
|
"reference_name": so1.name,
|
||||||
"is_advance": "Yes"
|
"is_advance": "Yes"
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
self.create_against_jv(jv_test_records[0], {
|
self.create_against_jv(jv_test_records[0], {
|
||||||
"party": "_Test Customer 3",
|
"party": "_Test Customer 3",
|
||||||
"against_sales_order": so2.name,
|
"reference_type": "Sales Order",
|
||||||
|
"reference_name": so2.name,
|
||||||
"credit": 1000,
|
"credit": 1000,
|
||||||
"is_advance": "Yes"
|
"is_advance": "Yes"
|
||||||
})
|
})
|
||||||
@@ -52,7 +54,8 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
self.create_against_jv(jv_test_records[0], {
|
self.create_against_jv(jv_test_records[0], {
|
||||||
"party": "_Test Customer 3",
|
"party": "_Test Customer 3",
|
||||||
"against_invoice": si1.name
|
"reference_type": si1.doctype,
|
||||||
|
"reference_name": si1.name
|
||||||
})
|
})
|
||||||
#Create SI with no outstanding
|
#Create SI with no outstanding
|
||||||
si2 = self.create_voucher(si_test_records[0], {
|
si2 = self.create_voucher(si_test_records[0], {
|
||||||
@@ -62,7 +65,8 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
self.create_against_jv(jv_test_records[0], {
|
self.create_against_jv(jv_test_records[0], {
|
||||||
"party": "_Test Customer 3",
|
"party": "_Test Customer 3",
|
||||||
"against_invoice": si2.name,
|
"reference_type": si2.doctype,
|
||||||
|
"reference_name": si2.name,
|
||||||
"credit": 561.80
|
"credit": 561.80
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -153,29 +157,12 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
new_jv = paytool.make_journal_entry()
|
new_jv = paytool.make_journal_entry()
|
||||||
|
|
||||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
|
||||||
#against_voucher, against_sales_order, against_purchase_order]
|
|
||||||
expected_values = [
|
|
||||||
[paytool.party_account, paytool.party, 100.00, expected_outstanding.get("Journal Entry")[0], None, None, None, None],
|
|
||||||
[paytool.party_account, paytool.party, 100.00, None, expected_outstanding.get("Sales Invoice")[0], None, None, None],
|
|
||||||
[paytool.party_account, paytool.party, 100.00, None, None, expected_outstanding.get("Purchase Invoice")[0], None, None],
|
|
||||||
[paytool.party_account, paytool.party, 100.00, None, None, None, expected_outstanding.get("Sales Order")[0], None],
|
|
||||||
[paytool.party_account, paytool.party, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
|
||||||
]
|
|
||||||
|
|
||||||
for jv_entry in new_jv.get("accounts"):
|
for jv_entry in new_jv.get("accounts"):
|
||||||
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
|
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
|
||||||
row = [
|
self.assertEquals(100.00,
|
||||||
jv_entry.get("account"),
|
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"))
|
||||||
jv_entry.get("party"),
|
self.assertEquals(jv_entry.reference_name,
|
||||||
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"),
|
expected_outstanding[jv_entry.reference_type][0])
|
||||||
jv_entry.get("against_jv"),
|
|
||||||
jv_entry.get("against_invoice"),
|
|
||||||
jv_entry.get("against_voucher"),
|
|
||||||
jv_entry.get("against_sales_order"),
|
|
||||||
jv_entry.get("against_purchase_order"),
|
|
||||||
]
|
|
||||||
self.assertTrue(row in expected_values)
|
|
||||||
|
|
||||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from frappe.model.document import Document
|
|||||||
class POSProfile(Document):
|
class POSProfile(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.check_for_duplicate()
|
self.check_for_duplicate()
|
||||||
self.validate_expense_account()
|
|
||||||
self.validate_all_link_fields()
|
self.validate_all_link_fields()
|
||||||
|
|
||||||
def check_for_duplicate(self):
|
def check_for_duplicate(self):
|
||||||
@@ -26,11 +25,6 @@ class POSProfile(Document):
|
|||||||
msgprint(_("Global POS Profile {0} already created for company {1}").format(res[0][0],
|
msgprint(_("Global POS Profile {0} already created for company {1}").format(res[0][0],
|
||||||
self.company), raise_exception=1)
|
self.company), raise_exception=1)
|
||||||
|
|
||||||
def validate_expense_account(self):
|
|
||||||
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
|
|
||||||
and not self.expense_account:
|
|
||||||
msgprint(_("Expense Account is mandatory"), raise_exception=1)
|
|
||||||
|
|
||||||
def validate_all_link_fields(self):
|
def validate_all_link_fields(self):
|
||||||
accounts = {"Account": [self.cash_bank_account, self.income_account,
|
accounts = {"Account": [self.cash_bank_account, self.income_account,
|
||||||
self.expense_account], "Cost Center": [self.cost_center],
|
self.expense_account], "Cost Center": [self.cost_center],
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -147,6 +147,7 @@ def get_pricing_rule_for_item(args):
|
|||||||
|
|
||||||
if pricing_rule:
|
if pricing_rule:
|
||||||
item_details.pricing_rule = pricing_rule.name
|
item_details.pricing_rule = pricing_rule.name
|
||||||
|
item_details.pricing_rule_for = pricing_rule.price_or_discount
|
||||||
if pricing_rule.price_or_discount == "Price":
|
if pricing_rule.price_or_discount == "Price":
|
||||||
item_details.update({
|
item_details.update({
|
||||||
"price_list_rate": pricing_rule.price/flt(args.conversion_rate) \
|
"price_list_rate": pricing_rule.price/flt(args.conversion_rate) \
|
||||||
|
|||||||
@@ -25,10 +25,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
if(!doc.is_return) {
|
if(!doc.is_return) {
|
||||||
if(doc.docstatus==1) {
|
if(doc.docstatus==1) {
|
||||||
if(doc.outstanding_amount > 0) {
|
if(doc.outstanding_amount > 0) {
|
||||||
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry);
|
this.frm.add_custom_button(__('Payment'), this.make_bank_entry).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
cur_frm.add_custom_button(__('Debit Note'), this.make_debit_note);
|
||||||
cur_frm.add_custom_button(__('Make Debit Note'), this.make_debit_note);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus===0) {
|
if(doc.docstatus===0) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.po_required()
|
self.po_required()
|
||||||
self.pr_required()
|
self.pr_required()
|
||||||
self.validate_supplier_invoice()
|
self.validate_supplier_invoice()
|
||||||
self.validate_advance_jv("advances", "purchase_order")
|
self.validate_advance_jv("Purchase Order")
|
||||||
|
|
||||||
self.check_active_purchase_items()
|
self.check_active_purchase_items()
|
||||||
self.check_conversion_rate()
|
self.check_conversion_rate()
|
||||||
@@ -365,7 +365,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
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, "against_voucher")
|
remove_against_link_from_jv(self.doctype, self.name)
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
|
|||||||
@@ -218,17 +218,14 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.load_from_db()
|
pi.load_from_db()
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_voucher=%s""", pi.name))
|
where reference_type='Purchase Invoice' and reference_name=%s and debit=300""", pi.name))
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
|
||||||
where against_voucher=%s and debit=300""", pi.name))
|
|
||||||
|
|
||||||
self.assertEqual(pi.outstanding_amount, 1212.30)
|
self.assertEqual(pi.outstanding_amount, 1212.30)
|
||||||
|
|
||||||
pi.cancel()
|
pi.cancel()
|
||||||
|
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_voucher=%s""", pi.name))
|
where reference_type='Purchase Invoice' and reference_name=%s""", pi.name))
|
||||||
|
|
||||||
def test_recurring_invoice(self):
|
def test_recurring_invoice(self):
|
||||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ 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) {
|
||||||
|
cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'),
|
||||||
|
this.make_sales_return);
|
||||||
|
|
||||||
if(cint(doc.update_stock)!=1) {
|
if(cint(doc.update_stock)!=1) {
|
||||||
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
||||||
var from_delivery_note = false;
|
var from_delivery_note = false;
|
||||||
@@ -57,16 +60,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(!from_delivery_note) {
|
if(!from_delivery_note) {
|
||||||
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'])
|
cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
||||||
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry);
|
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.add_custom_button(doc.update_stock ? __('Make Sales Return') : __('Make Credit Note'),
|
|
||||||
this.make_sales_return);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show buttons only when pos view is active
|
// Show buttons only when pos view is active
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class SalesInvoice(SellingController):
|
|||||||
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")
|
||||||
self.validate_advance_jv("advances", "sales_order")
|
self.validate_advance_jv("Sales Order")
|
||||||
self.add_remarks()
|
self.add_remarks()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.check_stop_sales_order("sales_order")
|
self.check_stop_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, "against_invoice")
|
remove_against_link_from_jv(self.doctype, self.name)
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
|
|||||||
@@ -391,7 +391,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
import test_records as jv_test_records
|
import test_records as jv_test_records
|
||||||
|
|
||||||
jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0]))
|
jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0]))
|
||||||
jv.get("accounts")[0].against_invoice = w.name
|
jv.get("accounts")[0].reference_type = w.doctype
|
||||||
|
jv.get("accounts")[0].reference_name = w.name
|
||||||
jv.insert()
|
jv.insert()
|
||||||
jv.submit()
|
jv.submit()
|
||||||
|
|
||||||
@@ -656,17 +657,17 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.load_from_db()
|
si.load_from_db()
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_invoice=%s""", si.name))
|
where reference_name=%s""", si.name))
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_invoice=%s and credit=300""", si.name))
|
where reference_name=%s and credit=300""", si.name))
|
||||||
|
|
||||||
self.assertEqual(si.outstanding_amount, 261.8)
|
self.assertEqual(si.outstanding_amount, 261.8)
|
||||||
|
|
||||||
si.cancel()
|
si.cancel()
|
||||||
|
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||||
where against_invoice=%s""", si.name))
|
where reference_name=%s""", si.name))
|
||||||
|
|
||||||
def test_recurring_invoice(self):
|
def test_recurring_invoice(self):
|
||||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||||
@@ -745,8 +746,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_return_sales_invoice(self):
|
def test_return_sales_invoice(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
|
|
||||||
|
|
||||||
actual_qty_0 = get_qty_after_transaction()
|
actual_qty_0 = get_qty_after_transaction()
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ from erpnext.accounts.utils import validate_expense_against_budget
|
|||||||
|
|
||||||
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
||||||
|
|
||||||
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True,
|
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'):
|
||||||
update_outstanding='Yes'):
|
|
||||||
if gl_map:
|
if gl_map:
|
||||||
if not cancel:
|
if not cancel:
|
||||||
gl_map = process_gl_map(gl_map, merge_entries)
|
gl_map = process_gl_map(gl_map, merge_entries)
|
||||||
@@ -51,7 +50,7 @@ def merge_similar_entries(gl_map):
|
|||||||
merged_gl_map.append(entry)
|
merged_gl_map.append(entry)
|
||||||
|
|
||||||
# filter zero debit and credit entries
|
# filter zero debit and credit entries
|
||||||
merged_gl_map = filter(lambda x: flt(x.debit)!=0 or flt(x.credit)!=0, merged_gl_map)
|
merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
|
||||||
return merged_gl_map
|
return merged_gl_map
|
||||||
|
|
||||||
def check_if_in_list(gle, gl_map):
|
def check_if_in_list(gle, gl_map):
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ erpnext.AccountsChart = Class.extend({
|
|||||||
title:__('New Account'),
|
title:__('New Account'),
|
||||||
fields: [
|
fields: [
|
||||||
{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
|
{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
|
||||||
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers, they are created automatically from the Customer and Supplier master")},
|
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")},
|
||||||
{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'),
|
||||||
|
|||||||
@@ -15,21 +15,19 @@ def execute(filters=None):
|
|||||||
entries = get_entries(filters)
|
entries = get_entries(filters)
|
||||||
invoice_posting_date_map = get_invoice_posting_date_map(filters)
|
invoice_posting_date_map = get_invoice_posting_date_map(filters)
|
||||||
against_date = ""
|
against_date = ""
|
||||||
outstanding_amount = 0.0
|
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for d in entries:
|
for d in entries:
|
||||||
if d.against_voucher:
|
against_date = invoice_posting_date_map[d.reference_name] or ""
|
||||||
against_date = d.against_voucher and invoice_posting_date_map[d.against_voucher] or ""
|
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:
|
||||||
against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or ""
|
|
||||||
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.against_voucher or d.against_invoice,
|
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name,
|
||||||
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
||||||
|
|
||||||
if d.against_voucher or d.against_invoice:
|
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 += ["", "", "", "", ""]
|
||||||
@@ -82,7 +80,7 @@ def get_conditions(filters):
|
|||||||
def get_entries(filters):
|
def get_entries(filters):
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date,
|
entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date,
|
||||||
jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit,
|
jvd.reference_type, jvd.reference_name, jvd.debit, jvd.credit,
|
||||||
jv.cheque_no, jv.cheque_date, jv.remark
|
jv.cheque_no, jv.cheque_date, jv.remark
|
||||||
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
|
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
|
||||||
where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
|
where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
|
||||||
|
|||||||
@@ -142,13 +142,6 @@ def reconcile_against_document(args):
|
|||||||
for d in args:
|
for d in args:
|
||||||
check_if_jv_modified(d)
|
check_if_jv_modified(d)
|
||||||
validate_allocated_amount(d)
|
validate_allocated_amount(d)
|
||||||
against_fld = {
|
|
||||||
'Journal Entry' : 'against_jv',
|
|
||||||
'Sales Invoice' : 'against_invoice',
|
|
||||||
'Purchase Invoice' : 'against_voucher'
|
|
||||||
}
|
|
||||||
|
|
||||||
d['against_fld'] = against_fld[d['against_voucher_type']]
|
|
||||||
|
|
||||||
# cancel JV
|
# cancel JV
|
||||||
jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
|
jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
|
||||||
@@ -173,8 +166,7 @@ def check_if_jv_modified(args):
|
|||||||
select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
|
select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where t1.name = t2.parent and t2.account = %(account)s
|
where t1.name = t2.parent and t2.account = %(account)s
|
||||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||||
and ifnull(t2.against_voucher, '')=''
|
and ifnull(t2.reference_type, '') in ("", "Sales Order", "Purchase Order")
|
||||||
and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')=''
|
|
||||||
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
||||||
and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
|
and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
|
||||||
|
|
||||||
@@ -193,7 +185,12 @@ def update_against_doc(d, jv_obj):
|
|||||||
"""
|
"""
|
||||||
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||||
jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
|
jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
|
||||||
jv_detail.set(d["against_fld"], d["against_voucher"])
|
|
||||||
|
original_reference_type = jv_detail.reference_type
|
||||||
|
original_reference_name = jv_detail.reference_name
|
||||||
|
|
||||||
|
jv_detail.set("reference_type", d["against_voucher_type"])
|
||||||
|
jv_detail.set("reference_name", d["against_voucher"])
|
||||||
|
|
||||||
if d['allocated_amt'] < d['unadjusted_amt']:
|
if d['allocated_amt'] < d['unadjusted_amt']:
|
||||||
jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance
|
jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance
|
||||||
@@ -208,6 +205,8 @@ def update_against_doc(d, jv_obj):
|
|||||||
ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt']))
|
ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt']))
|
||||||
ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0)
|
ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0)
|
||||||
ch.against_account = cstr(jvd[0][2])
|
ch.against_account = cstr(jvd[0][2])
|
||||||
|
ch.reference_type = original_reference_type
|
||||||
|
ch.reference_name = original_reference_name
|
||||||
ch.is_advance = cstr(jvd[0][3])
|
ch.is_advance = cstr(jvd[0][3])
|
||||||
ch.docstatus = 1
|
ch.docstatus = 1
|
||||||
|
|
||||||
@@ -215,15 +214,16 @@ def update_against_doc(d, jv_obj):
|
|||||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
jv_obj.flags.ignore_validate_update_after_submit = True
|
||||||
jv_obj.save()
|
jv_obj.save()
|
||||||
|
|
||||||
def remove_against_link_from_jv(ref_type, ref_no, against_field):
|
def remove_against_link_from_jv(ref_type, ref_no):
|
||||||
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
|
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
|
||||||
where `%s`=%s and docstatus < 2""" % (against_field, "%s"), (ref_no))
|
where reference_type=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
|
||||||
|
|
||||||
if linked_jv:
|
if linked_jv:
|
||||||
frappe.db.sql("""update `tabJournal Entry Account` set `%s`=null,
|
frappe.db.sql("""update `tabJournal Entry Account`
|
||||||
|
set reference_type=null, reference_name = null,
|
||||||
modified=%s, modified_by=%s
|
modified=%s, modified_by=%s
|
||||||
where `%s`=%s and docstatus < 2""" % (against_field, "%s", "%s", against_field, "%s"),
|
where reference_type=%s and reference_name=%s
|
||||||
(now(), frappe.session.user, ref_no))
|
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
||||||
|
|
||||||
frappe.db.sql("""update `tabGL Entry`
|
frappe.db.sql("""update `tabGL Entry`
|
||||||
set against_voucher_type=null, against_voucher=null,
|
set against_voucher_type=null, against_voucher=null,
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ frappe.provide("erpnext.buying");
|
|||||||
|
|
||||||
{% include 'buying/doctype/purchase_common/purchase_common.js' %};
|
{% include 'buying/doctype/purchase_common/purchase_common.js' %};
|
||||||
|
|
||||||
|
frappe.ui.form.on("Purchase Order", {
|
||||||
|
onload: function(frm) {
|
||||||
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||||
refresh: function(doc, cdt, cdn) {
|
refresh: function(doc, cdt, cdn) {
|
||||||
var me = this;
|
var me = this;
|
||||||
@@ -12,25 +20,32 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
// this.frm.dashboard.reset();
|
// this.frm.dashboard.reset();
|
||||||
|
|
||||||
if(doc.docstatus == 1 && doc.status != 'Stopped') {
|
if(doc.docstatus == 1 && doc.status != 'Stopped') {
|
||||||
|
|
||||||
|
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
||||||
|
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
||||||
|
|
||||||
|
if(flt(doc.per_billed)==0) {
|
||||||
|
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||||
|
}
|
||||||
|
|
||||||
if(flt(doc.per_received, 2) < 100) {
|
if(flt(doc.per_received, 2) < 100) {
|
||||||
cur_frm.add_custom_button(__('Make Purchase Receipt'), this.make_purchase_receipt);
|
cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary");
|
||||||
|
|
||||||
if(doc.is_subcontracted==="Yes") {
|
if(doc.is_subcontracted==="Yes") {
|
||||||
cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry);
|
cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(flt(doc.per_billed, 2) < 100)
|
|
||||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice);
|
|
||||||
|
|
||||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
if(flt(doc.per_billed, 2) < 100)
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice);
|
||||||
|
|
||||||
|
|
||||||
} else if(doc.docstatus===0) {
|
} else if(doc.docstatus===0) {
|
||||||
cur_frm.cscript.add_from_mappers();
|
cur_frm.cscript.add_from_mappers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
||||||
cur_frm.add_custom_button(__('Unstop Purchase Order'), cur_frm.cscript['Unstop Purchase Order']);
|
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']);
|
||||||
},
|
},
|
||||||
|
|
||||||
make_stock_entry: function() {
|
make_stock_entry: function() {
|
||||||
@@ -126,7 +141,21 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
items_add: function(doc, cdt, cdn) {
|
items_add: function(doc, cdt, cdn) {
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
var row = frappe.get_doc(cdt, cdn);
|
||||||
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
||||||
|
},
|
||||||
|
|
||||||
|
make_bank_entry: function() {
|
||||||
|
return frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_order",
|
||||||
|
args: {
|
||||||
|
"purchase_order": cur_frm.doc.name
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// for backward compatibility: combine new and previous states
|
// for backward compatibility: combine new and previous states
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ cur_frm.cscript.make_dashboard = function(doc) {
|
|||||||
+ '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>"
|
+ '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>"
|
||||||
+ format_currency(r.message.total_unpaid, r.message.company_currency[0])
|
+ format_currency(r.message.total_unpaid, r.message.company_currency[0])
|
||||||
+ '</b></span>');
|
+ '</b></span>');
|
||||||
|
} else {
|
||||||
|
cur_frm.dashboard.set_headline("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur_frm.dashboard.set_badge_count(r.message);
|
cur_frm.dashboard.set_badge_count(r.message);
|
||||||
|
|||||||
6
erpnext/change_log/v5/v5_6_0.md
Normal file
6
erpnext/change_log/v5/v5_6_0.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- For referencing a line in **Journal Entry**, now you can reference by the **Reference Type** and **Reference Name** columns, instead of "Against Sales Invoice", "Against Purchase Invoice", etc.
|
||||||
|
- Additional Costs in Stock Entry **[Sponsored by PT. Ridho Sribumi Sejahtera]**
|
||||||
|
Now additional costs like shipping charges, operating costs etc can be added in Stock Entry in item valuation
|
||||||
|
- **Update Finished Goods** in Production Order can now use the items from **Transfer Materials for Manufacture** step instead of items from the Bill of Materials. This can be configured in Manufacturing Settings
|
||||||
|
- Added field **Tax ID** in Customer
|
||||||
|
- Bug fixes in Item, Time Log Batch, Pricing Rule, Salary Slip, Address and Stock Entry
|
||||||
@@ -11,7 +11,7 @@ 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
|
||||||
|
|
||||||
force_item_fields = ("item_name", "item_group", "barcode", "brand", "stock_uom")
|
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||||
|
|
||||||
class CustomerFrozen(frappe.ValidationError): pass
|
class CustomerFrozen(frappe.ValidationError): pass
|
||||||
|
|
||||||
@@ -153,9 +153,10 @@ class AccountsController(TransactionBase):
|
|||||||
item.set(fieldname, value)
|
item.set(fieldname, value)
|
||||||
|
|
||||||
if ret.get("pricing_rule"):
|
if ret.get("pricing_rule"):
|
||||||
for field in ["base_price_list_rate", "price_list_rate",
|
item.set("discount_percentage", ret.get("discount_percentage"))
|
||||||
"discount_percentage", "base_rate", "rate"]:
|
if ret.get("pricing_rule_for") == "Price":
|
||||||
item.set(field, ret.get(field))
|
item.set("pricing_list_rate", ret.get("pricing_list_rate"))
|
||||||
|
|
||||||
|
|
||||||
def set_taxes(self):
|
def set_taxes(self):
|
||||||
if not self.meta.get_field("taxes"):
|
if not self.meta.get_field("taxes"):
|
||||||
@@ -211,29 +212,32 @@ class AccountsController(TransactionBase):
|
|||||||
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||||
|
|
||||||
def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
|
def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
|
||||||
so_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
"""Returns list of advances against Account, Party, Reference"""
|
||||||
cond = ""
|
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
||||||
if so_list:
|
|
||||||
cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list)))
|
if not order_list:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_placeholder = ', '.join(['%s'] * len(order_list))
|
||||||
|
|
||||||
|
# conver sales_order to "Sales Order"
|
||||||
|
reference_type = against_order_field.replace("_", " ").title()
|
||||||
|
|
||||||
res = frappe.db.sql("""
|
res = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no, `against_{1}` as against_order
|
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
|
||||||
|
reference_name as against_order
|
||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where
|
where
|
||||||
t1.name = t2.parent and t2.account = %s
|
t1.name = t2.parent and t2.account = %s
|
||||||
and t2.party_type=%s and t2.party=%s
|
and t2.party_type = %s and t2.party = %s
|
||||||
and t2.is_advance = 'Yes' and t1.docstatus = 1
|
and t2.is_advance = 'Yes' and t1.docstatus = 1
|
||||||
and ((
|
and (
|
||||||
ifnull(t2.against_voucher, '') = ''
|
ifnull(t2.reference_type, '')=''
|
||||||
and ifnull(t2.against_invoice, '') = ''
|
or (t2.reference_type = %s and ifnull(t2.reference_name, '') in ({1})))
|
||||||
and ifnull(t2.against_jv, '') = ''
|
order by t1.posting_date""".format(dr_or_cr, in_placeholder),
|
||||||
and ifnull(t2.against_sales_order, '') = ''
|
[account_head, party_type, party, reference_type] + order_list, as_dict=1)
|
||||||
and ifnull(t2.against_purchase_order, '') = ''
|
|
||||||
) {2})
|
|
||||||
order by t1.posting_date""".format(dr_or_cr, against_order_field, cond),
|
|
||||||
[account_head, party_type, party] + so_list, as_dict=1)
|
|
||||||
|
|
||||||
self.set(parentfield, [])
|
self.set(parentfield, [])
|
||||||
for d in res:
|
for d in res:
|
||||||
@@ -246,25 +250,26 @@ class AccountsController(TransactionBase):
|
|||||||
"allocated_amount": flt(d.amount) if d.against_order else 0
|
"allocated_amount": flt(d.amount) if d.against_order else 0
|
||||||
})
|
})
|
||||||
|
|
||||||
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
def validate_advance_jv(self, reference_type):
|
||||||
|
against_order_field = frappe.scrub(reference_type)
|
||||||
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
||||||
if order_list:
|
if order_list:
|
||||||
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
|
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
|
||||||
|
|
||||||
jv_against_order = frappe.db.sql("""select parent, %s as against_order
|
jv_against_order = frappe.db.sql("""select parent, reference_name as against_order
|
||||||
from `tabJournal Entry Account`
|
from `tabJournal Entry Account`
|
||||||
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
|
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
|
||||||
and ifnull(against_sales_order, '') in (%s)
|
and reference_type=%s
|
||||||
group by parent, against_sales_order""" %
|
and ifnull(reference_name, '') in ({0})
|
||||||
("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))),
|
group by parent, reference_name""".format(', '.join(['%s']*len(order_list))),
|
||||||
tuple([account] + order_list), as_dict=1)
|
tuple([account, reference_type] + order_list), as_dict=1)
|
||||||
|
|
||||||
if jv_against_order:
|
if jv_against_order:
|
||||||
order_jv_map = {}
|
order_jv_map = {}
|
||||||
for d in jv_against_order:
|
for d in jv_against_order:
|
||||||
order_jv_map.setdefault(d.against_order, []).append(d.parent)
|
order_jv_map.setdefault(d.against_order, []).append(d.parent)
|
||||||
|
|
||||||
advance_jv_against_si = [d.journal_entry for d in self.get(advance_table_fieldname)]
|
advance_jv_against_si = [d.journal_entry for d in self.get("advances")]
|
||||||
|
|
||||||
for order, jv_list in order_jv_map.items():
|
for order, jv_list in order_jv_map.items():
|
||||||
for jv in jv_list:
|
for jv in jv_list:
|
||||||
@@ -318,10 +323,8 @@ class AccountsController(TransactionBase):
|
|||||||
def set_total_advance_paid(self):
|
def set_total_advance_paid(self):
|
||||||
if self.doctype == "Sales Order":
|
if self.doctype == "Sales Order":
|
||||||
dr_or_cr = "credit"
|
dr_or_cr = "credit"
|
||||||
against_field = "against_sales_order"
|
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "debit"
|
dr_or_cr = "debit"
|
||||||
against_field = "against_purchase_order"
|
|
||||||
|
|
||||||
advance_paid = frappe.db.sql("""
|
advance_paid = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
@@ -329,8 +332,10 @@ class AccountsController(TransactionBase):
|
|||||||
from
|
from
|
||||||
`tabJournal Entry Account`
|
`tabJournal Entry Account`
|
||||||
where
|
where
|
||||||
{against_field} = %s and docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr, \
|
reference_type = %s and
|
||||||
against_field=against_field), self.name)
|
reference_name = %s and
|
||||||
|
docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr),
|
||||||
|
(self.doctype, self.name))
|
||||||
|
|
||||||
if advance_paid:
|
if advance_paid:
|
||||||
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
|
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
|
||||||
|
|||||||
@@ -80,9 +80,23 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
|||||||
if(doc.status!=="Quotation")
|
if(doc.status!=="Quotation")
|
||||||
cur_frm.add_custom_button(__('Opportunity Lost'),
|
cur_frm.add_custom_button(__('Opportunity Lost'),
|
||||||
cur_frm.cscript['Declare Opportunity Lost'], "icon-remove", "btn-default");
|
cur_frm.cscript['Declare Opportunity Lost'], "icon-remove", "btn-default");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var frm = cur_frm;
|
||||||
|
if(frm.perm[0].write && doc.docstatus==0) {
|
||||||
|
if(frm.doc.status==="Open") {
|
||||||
|
frm.add_custom_button("Close", function() {
|
||||||
|
frm.set_value("status", "Closed");
|
||||||
|
frm.save();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.add_custom_button("Reopen", function() {
|
||||||
|
frm.set_value("status", "Open");
|
||||||
|
frm.save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ blogs.
|
|||||||
"""
|
"""
|
||||||
app_icon = "icon-th"
|
app_icon = "icon-th"
|
||||||
app_color = "#e74c3c"
|
app_color = "#e74c3c"
|
||||||
app_version = "5.5.1"
|
app_version = "5.6.0"
|
||||||
github_link = "https://github.com/frappe/erpnext"
|
github_link = "https://github.com/frappe/erpnext"
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
|||||||
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.debit = expense[i].sanctioned_amount;
|
||||||
d1.account = expense[i].default_account;
|
d1.account = expense[i].default_account;
|
||||||
d1.against_expense_claim = cur_frm.doc.name;
|
d1.reference_type = cur_frm.doc.doctype;
|
||||||
|
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 = cur_frm.doc.total_sanctioned_amount;
|
||||||
d1.against_expense_claim = cur_frm.doc.name;
|
d1.reference_type = cur_frm.doc.doctype;
|
||||||
|
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;
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ class SalarySlip(TransactionBase):
|
|||||||
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
|
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
|
||||||
for d in self.get("earnings"):
|
for d in self.get("earnings"):
|
||||||
if cint(d.e_depends_on_lwp) == 1:
|
if cint(d.e_depends_on_lwp) == 1:
|
||||||
d.e_modified_amount = rounded(flt(d.e_amount) * flt(self.payment_days)
|
d.e_modified_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
|
||||||
/ cint(self.total_days_in_month), 2)
|
/ cint(self.total_days_in_month)), self.precision("e_modified_amount", "earnings"))
|
||||||
elif not self.payment_days:
|
elif not self.payment_days:
|
||||||
d.e_modified_amount = 0
|
d.e_modified_amount = 0
|
||||||
elif not d.e_modified_amount:
|
elif not d.e_modified_amount:
|
||||||
@@ -164,8 +164,8 @@ class SalarySlip(TransactionBase):
|
|||||||
self.total_deduction = 0
|
self.total_deduction = 0
|
||||||
for d in self.get('deductions'):
|
for d in self.get('deductions'):
|
||||||
if cint(d.d_depends_on_lwp) == 1:
|
if cint(d.d_depends_on_lwp) == 1:
|
||||||
d.d_modified_amount = rounded(flt(d.d_amount) * flt(self.payment_days)
|
d.d_modified_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
|
||||||
/ cint(self.total_days_in_month), 2)
|
/ cint(self.total_days_in_month)), self.precision("d_modified_amount", "deductions"))
|
||||||
elif not self.payment_days:
|
elif not self.payment_days:
|
||||||
d.d_modified_amount = 0
|
d.d_modified_amount = 0
|
||||||
elif not d.d_modified_amount:
|
elif not d.d_modified_amount:
|
||||||
@@ -174,10 +174,13 @@ class SalarySlip(TransactionBase):
|
|||||||
self.total_deduction += flt(d.d_modified_amount)
|
self.total_deduction += flt(d.d_modified_amount)
|
||||||
|
|
||||||
def calculate_net_pay(self):
|
def calculate_net_pay(self):
|
||||||
|
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
||||||
|
|
||||||
self.calculate_earning_total()
|
self.calculate_earning_total()
|
||||||
self.calculate_ded_total()
|
self.calculate_ded_total()
|
||||||
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
|
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
|
||||||
self.rounded_total = rounded(self.net_pay)
|
self.rounded_total = rounded(self.net_pay,
|
||||||
|
self.precision("net_pay") if disable_rounded_total else 0)
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if(self.email_check == 1):
|
if(self.email_check == 1):
|
||||||
|
|||||||
@@ -41,6 +41,15 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"default": "BOM",
|
||||||
|
"fieldname": "backflush_raw_materials_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Backflush Raw Materials Based On",
|
||||||
|
"options": "BOM\nMaterial Transferred for Manufacture",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import cint
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
class ManufacturingSettings(Document):
|
class ManufacturingSettings(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_mins_between_operations():
|
||||||
|
if not hasattr(frappe.local, "_mins_between_operations"):
|
||||||
|
frappe.local._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings",
|
||||||
|
"mins_between_operations")) or 10
|
||||||
|
return relativedelta(minutes=frappe.local._mins_between_operations)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ $.extend(cur_frm.cscript, {
|
|||||||
make_se: function(purpose) {
|
make_se: function(purpose) {
|
||||||
var me = this;
|
var me = this;
|
||||||
var max = (purpose === "Manufacture") ?
|
var max = (purpose === "Manufacture") ?
|
||||||
flt(this.frm.doc.qty) - flt(this.frm.doc.produced_qty) :
|
flt(this.frm.doc.material_transferred_for_manufacturing) - flt(this.frm.doc.produced_qty) :
|
||||||
flt(this.frm.doc.qty) - flt(this.frm.doc.material_transferred_for_manufacturing);
|
flt(this.frm.doc.qty) - flt(this.frm.doc.material_transferred_for_manufacturing);
|
||||||
|
|
||||||
frappe.prompt({fieldtype:"Int", label: __("Qty for {0}", [purpose]), fieldname:"qty",
|
frappe.prompt({fieldtype:"Int", label: __("Qty for {0}", [purpose]), fieldname:"qty",
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ 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
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||||
|
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
|
||||||
|
from erpnext.projects.doctype.time_log.time_log import OverlapError
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
|
||||||
|
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||||
|
|
||||||
class OverProductionError(frappe.ValidationError): pass
|
class OverProductionError(frappe.ValidationError): pass
|
||||||
class StockOverProductionError(frappe.ValidationError): pass
|
class StockOverProductionError(frappe.ValidationError): pass
|
||||||
@@ -17,9 +21,6 @@ class OperationTooLongError(frappe.ValidationError): pass
|
|||||||
class ProductionNotApplicableError(frappe.ValidationError): pass
|
class ProductionNotApplicableError(frappe.ValidationError): pass
|
||||||
class ItemHasVariantError(frappe.ValidationError): pass
|
class ItemHasVariantError(frappe.ValidationError): pass
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
|
|
||||||
from erpnext.projects.doctype.time_log.time_log import OverlapError
|
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"operations": "templates/form_grid/production_order_grid.html"
|
"operations": "templates/form_grid/production_order_grid.html"
|
||||||
}
|
}
|
||||||
@@ -231,6 +232,7 @@ class ProductionOrder(Document):
|
|||||||
original_start_time = time_log.from_time
|
original_start_time = time_log.from_time
|
||||||
while True:
|
while True:
|
||||||
_from_time = time_log.from_time
|
_from_time = time_log.from_time
|
||||||
|
|
||||||
try:
|
try:
|
||||||
time_log.save()
|
time_log.save()
|
||||||
break
|
break
|
||||||
@@ -248,6 +250,7 @@ class ProductionOrder(Document):
|
|||||||
frappe.msgprint(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
|
frappe.msgprint(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# if time log needs to be moved, make sure that the from time is not the same
|
||||||
if _from_time == time_log.from_time:
|
if _from_time == time_log.from_time:
|
||||||
frappe.throw("Capacity Planning Error")
|
frappe.throw("Capacity Planning Error")
|
||||||
|
|
||||||
@@ -273,19 +276,13 @@ class ProductionOrder(Document):
|
|||||||
d.planned_start_time = self.planned_start_date
|
d.planned_start_time = self.planned_start_date
|
||||||
else:
|
else:
|
||||||
d.planned_start_time = get_datetime(self.operations[i-1].planned_end_time)\
|
d.planned_start_time = get_datetime(self.operations[i-1].planned_end_time)\
|
||||||
+ self.get_mins_between_operations()
|
+ get_mins_between_operations()
|
||||||
|
|
||||||
d.planned_end_time = get_datetime(d.planned_start_time) + relativedelta(minutes = d.time_in_mins)
|
d.planned_end_time = get_datetime(d.planned_start_time) + relativedelta(minutes = d.time_in_mins)
|
||||||
|
|
||||||
if d.planned_start_time == d.planned_end_time:
|
if d.planned_start_time == d.planned_end_time:
|
||||||
frappe.throw(_("Capacity Planning Error"))
|
frappe.throw(_("Capacity Planning Error"))
|
||||||
|
|
||||||
def get_mins_between_operations(self):
|
|
||||||
if not hasattr(self, "_mins_between_operations"):
|
|
||||||
self._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings",
|
|
||||||
"mins_between_operations")) or 10
|
|
||||||
return relativedelta(minutes=self._mins_between_operations)
|
|
||||||
|
|
||||||
def check_operation_fits_in_working_hours(self, d):
|
def check_operation_fits_in_working_hours(self, d):
|
||||||
"""Raises expection if operation is longer than working hours in the given workstation."""
|
"""Raises expection if operation is longer than working hours in the given workstation."""
|
||||||
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
|
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
|
||||||
@@ -356,7 +353,6 @@ def make_stock_entry(production_order_id, purpose, qty=None):
|
|||||||
stock_entry.company = production_order.company
|
stock_entry.company = production_order.company
|
||||||
stock_entry.from_bom = 1
|
stock_entry.from_bom = 1
|
||||||
stock_entry.bom_no = production_order.bom_no
|
stock_entry.bom_no = production_order.bom_no
|
||||||
stock_entry.additional_operating_cost = production_order.additional_operating_cost
|
|
||||||
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
|
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
|
||||||
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
|
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
|
||||||
|
|
||||||
@@ -365,6 +361,8 @@ def make_stock_entry(production_order_id, purpose, qty=None):
|
|||||||
else:
|
else:
|
||||||
stock_entry.from_warehouse = production_order.wip_warehouse
|
stock_entry.from_warehouse = production_order.wip_warehouse
|
||||||
stock_entry.to_warehouse = production_order.fg_warehouse
|
stock_entry.to_warehouse = production_order.fg_warehouse
|
||||||
|
additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty)
|
||||||
|
stock_entry.set("additional_costs", additional_costs)
|
||||||
|
|
||||||
stock_entry.get_items()
|
stock_entry.get_items()
|
||||||
return stock_entry.as_dict()
|
return stock_entry.as_dict()
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ class TestProductionOrder(unittest.TestCase):
|
|||||||
|
|
||||||
# add raw materials to stores
|
# add raw materials to stores
|
||||||
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||||
target="Stores - _TC", qty=100, incoming_rate=100)
|
target="Stores - _TC", qty=100, basic_rate=100)
|
||||||
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||||
target="Stores - _TC", qty=100, incoming_rate=100)
|
target="Stores - _TC", qty=100, basic_rate=100)
|
||||||
|
|
||||||
# from stores to wip
|
# from stores to wip
|
||||||
s = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 4))
|
s = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 4))
|
||||||
@@ -58,9 +58,9 @@ class TestProductionOrder(unittest.TestCase):
|
|||||||
pro_doc = self.check_planned_qty()
|
pro_doc = self.check_planned_qty()
|
||||||
|
|
||||||
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||||
target="_Test Warehouse - _TC", qty=100, incoming_rate=100)
|
target="_Test Warehouse - _TC", qty=100, basic_rate=100)
|
||||||
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||||
target="_Test Warehouse - _TC", qty=100, incoming_rate=100)
|
target="_Test Warehouse - _TC", qty=100, basic_rate=100)
|
||||||
|
|
||||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
||||||
s.insert()
|
s.insert()
|
||||||
|
|||||||
@@ -188,3 +188,7 @@ erpnext.patches.v5_4.set_root_and_report_type
|
|||||||
erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
|
erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
|
||||||
erpnext.patches.v5_4.fix_invoice_outstanding
|
erpnext.patches.v5_4.fix_invoice_outstanding
|
||||||
execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0")
|
execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0")
|
||||||
|
erpnext.patches.v5_4.fix_missing_item_images
|
||||||
|
erpnext.patches.v5_4.stock_entry_additional_costs
|
||||||
|
erpnext.patches.v5_4.cleanup_journal_entry
|
||||||
|
execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''")
|
||||||
|
|||||||
15
erpnext/patches/v5_4/cleanup_journal_entry.py
Normal file
15
erpnext/patches/v5_4/cleanup_journal_entry.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doctype("Journal Entry Account")
|
||||||
|
for doctype, fieldname in (
|
||||||
|
("Sales Order", "against_sales_order"),
|
||||||
|
("Purchase Order", "against_purchase_order"),
|
||||||
|
("Sales Invoice", "against_invoice"),
|
||||||
|
("Purchase Invoice", "against_voucher"),
|
||||||
|
("Journal Entry", "against_jv"),
|
||||||
|
("Expense Claim", "against_expense_claim"),
|
||||||
|
):
|
||||||
|
frappe.db.sql("""update `tabJournal Entry Account`
|
||||||
|
set reference_type=%s and reference_name={0} where ifnull({0}, '') != ''
|
||||||
|
""".format(fieldname), doctype)
|
||||||
116
erpnext/patches/v5_4/fix_missing_item_images.py
Normal file
116
erpnext/patches/v5_4/fix_missing_item_images.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
import os
|
||||||
|
from frappe.utils import get_files_path
|
||||||
|
from frappe.utils.file_manager import get_content_hash
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
files_path = get_files_path()
|
||||||
|
|
||||||
|
# get files that don't have attached_to_name but exist
|
||||||
|
unlinked_files = get_unlinked_files(files_path)
|
||||||
|
if not unlinked_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
fixed_files = fix_files_for_item(files_path, unlinked_files)
|
||||||
|
|
||||||
|
# fix remaining files
|
||||||
|
for key, file_data in unlinked_files.items():
|
||||||
|
if key not in fixed_files:
|
||||||
|
rename_and_set_content_hash(files_path, unlinked_files, key)
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
def fix_files_for_item(files_path, unlinked_files):
|
||||||
|
fixed_files = []
|
||||||
|
|
||||||
|
# make a list of files/something and /files/something to check in child table's image column
|
||||||
|
file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()]
|
||||||
|
file_item_code = get_file_item_code(file_urls)
|
||||||
|
|
||||||
|
for (file_url, item_code), children in file_item_code.items():
|
||||||
|
new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"])
|
||||||
|
|
||||||
|
for row in children:
|
||||||
|
# print file_url, new_file_url, item_code, row.doctype, row.name
|
||||||
|
|
||||||
|
# replace image in these rows with the new file url
|
||||||
|
frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False)
|
||||||
|
|
||||||
|
# set it as attachment of this item code
|
||||||
|
file_data = frappe.get_doc("File Data", unlinked_files[file_url]["file"])
|
||||||
|
file_data.attached_to_doctype = "Item"
|
||||||
|
file_data.attached_to_name = item_code
|
||||||
|
file_data.save()
|
||||||
|
|
||||||
|
# set it as image in Item
|
||||||
|
if not frappe.db.get_value("Item", item_code, "image"):
|
||||||
|
frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False)
|
||||||
|
|
||||||
|
rename_and_set_content_hash(files_path, unlinked_files, file_url)
|
||||||
|
|
||||||
|
fixed_files.append(file_url)
|
||||||
|
|
||||||
|
# commit
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
return fixed_files
|
||||||
|
|
||||||
|
def rename_and_set_content_hash(files_path, unlinked_files, file_url):
|
||||||
|
# rename this file
|
||||||
|
old_filename = os.path.join(files_path, unlinked_files[file_url]["file"])
|
||||||
|
new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"])
|
||||||
|
|
||||||
|
if not os.path.exists(new_filename):
|
||||||
|
os.rename(old_filename, new_filename)
|
||||||
|
|
||||||
|
# set content hash if missing
|
||||||
|
file_data_name = unlinked_files[file_url]["file"]
|
||||||
|
if not frappe.db.get_value("File Data", file_data_name, "content_hash"):
|
||||||
|
with open(new_filename, "r") as f:
|
||||||
|
content_hash = get_content_hash(f.read())
|
||||||
|
frappe.db.set_value("File Data", file_data_name, "content_hash", content_hash)
|
||||||
|
|
||||||
|
def get_unlinked_files(files_path):
|
||||||
|
# find files that have the same name as a File Data doc
|
||||||
|
# and the file_name mentioned in that File Data doc doesn't exist
|
||||||
|
# and it isn't already attached to a doc
|
||||||
|
unlinked_files = {}
|
||||||
|
files = os.listdir(files_path)
|
||||||
|
for file in files:
|
||||||
|
if not frappe.db.exists("File Data", {"file_name": file}):
|
||||||
|
file_data = frappe.db.get_value("File Data", {"name": file},
|
||||||
|
["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True)
|
||||||
|
|
||||||
|
if (file_data
|
||||||
|
and file_data.file_name
|
||||||
|
and file_data.file_name not in files
|
||||||
|
and not file_data.attached_to_doctype
|
||||||
|
and not file_data.attached_to_name):
|
||||||
|
|
||||||
|
file_data["file"] = file
|
||||||
|
unlinked_files["files/{0}".format(file)] = file_data
|
||||||
|
|
||||||
|
return unlinked_files
|
||||||
|
|
||||||
|
def get_file_item_code(file_urls):
|
||||||
|
# get a map of file_url, item_code and list of documents where file_url will need to be changed in image field
|
||||||
|
file_item_code = {}
|
||||||
|
|
||||||
|
doctypes = frappe.db.sql_list("""select name from `tabDocType` dt
|
||||||
|
where istable=1
|
||||||
|
and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code')
|
||||||
|
and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""")
|
||||||
|
|
||||||
|
for doctype in doctypes:
|
||||||
|
result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}`
|
||||||
|
where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))),
|
||||||
|
file_urls, as_dict=True)
|
||||||
|
|
||||||
|
for r in result:
|
||||||
|
key = (r.image, r.item_code)
|
||||||
|
if key not in file_item_code:
|
||||||
|
file_item_code[key] = []
|
||||||
|
|
||||||
|
file_item_code[key].append(r)
|
||||||
|
|
||||||
|
return file_item_code
|
||||||
42
erpnext/patches/v5_4/stock_entry_additional_costs.py
Normal file
42
erpnext/patches/v5_4/stock_entry_additional_costs.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Copyright (c) 2015, Web Notes 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 flt
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doctype("Stock Entry")
|
||||||
|
frappe.reload_doctype("Stock Entry Detail")
|
||||||
|
frappe.reload_doctype("Landed Cost Taxes and Charges")
|
||||||
|
|
||||||
|
frappe.db.sql("""update `tabStock Entry Detail` sed, `tabStock Entry` se
|
||||||
|
set sed.valuation_rate=sed.incoming_rate, sed.basic_rate=sed.incoming_rate, sed.basic_amount=sed.amount
|
||||||
|
where sed.parent = se.name
|
||||||
|
and (se.purpose not in ('Manufacture', 'Repack') or ifnull(additional_operating_cost, 0)=0)
|
||||||
|
""")
|
||||||
|
|
||||||
|
stock_entries = frappe.db.sql_list("""select name from `tabStock Entry`
|
||||||
|
where purpose in ('Manufacture', 'Repack') and ifnull(additional_operating_cost, 0)!=0""")
|
||||||
|
|
||||||
|
for d in stock_entries:
|
||||||
|
stock_entry = frappe.get_doc("Stock Entry", d)
|
||||||
|
stock_entry.append("additional_costs", {
|
||||||
|
"description": "Additional Operating Cost",
|
||||||
|
"amount": stock_entry.additional_operating_cost
|
||||||
|
})
|
||||||
|
|
||||||
|
number_of_fg_items = len([t.t_warehouse for t in stock_entry.get("items") if t.t_warehouse])
|
||||||
|
|
||||||
|
for d in stock_entry.get("items"):
|
||||||
|
d.valuation_rate = d.incoming_rate
|
||||||
|
|
||||||
|
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||||
|
d.additional_cost = stock_entry.additional_operating_cost
|
||||||
|
|
||||||
|
d.basic_rate = flt(d.valuation_rate) - flt(d.additional_cost)
|
||||||
|
d.basic_amount = flt(flt(d.basic_rate) *flt(d.transfer_qty), d.precision("basic_amount"))
|
||||||
|
|
||||||
|
stock_entry.flags.ignore_validate = True
|
||||||
|
stock_entry.flags.ignore_validate_update_after_submit = True
|
||||||
|
stock_entry.save()
|
||||||
@@ -6,6 +6,7 @@ import frappe, json
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr, flt, get_datetime, get_time, getdate
|
from frappe.utils import cstr, flt, get_datetime, get_time, getdate
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||||
|
|
||||||
class OverlapError(frappe.ValidationError): pass
|
class OverlapError(frappe.ValidationError): pass
|
||||||
class OverProductionLoggedError(frappe.ValidationError): pass
|
class OverProductionLoggedError(frappe.ValidationError): pass
|
||||||
@@ -182,9 +183,14 @@ class TimeLog(Document):
|
|||||||
|
|
||||||
def move_to_next_non_overlapping_slot(self):
|
def move_to_next_non_overlapping_slot(self):
|
||||||
"""If in overlap, set start as the end point of the overlapping time log"""
|
"""If in overlap, set start as the end point of the overlapping time log"""
|
||||||
overlapping = self.get_overlap_for("workstation")
|
overlapping = self.get_overlap_for("workstation") \
|
||||||
if overlapping:
|
or self.get_overlap_for("employee") \
|
||||||
self.from_time = get_datetime(overlapping.to_time) + relativedelta(minutes=10)
|
or self.get_overlap_for("user")
|
||||||
|
|
||||||
|
if not overlapping:
|
||||||
|
frappe.throw("Logical error: Must find overlapping")
|
||||||
|
|
||||||
|
self.from_time = get_datetime(overlapping.to_time) + get_mins_between_operations()
|
||||||
|
|
||||||
def get_time_log_summary(self):
|
def get_time_log_summary(self):
|
||||||
"""Returns 'Actual Operating Time'. """
|
"""Returns 'Actual Operating Time'. """
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class TimeLogBatch(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
self.total_hours = 0.0
|
||||||
|
self.total_billing_amount = 0.0
|
||||||
for d in self.get("time_logs"):
|
for d in self.get("time_logs"):
|
||||||
tl = frappe.get_doc("Time Log", d.time_log)
|
tl = frappe.get_doc("Time Log", d.time_log)
|
||||||
self.update_time_log_values(d, tl)
|
self.update_time_log_values(d, tl)
|
||||||
|
|||||||
@@ -13,30 +13,8 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
|
|||||||
|
|
||||||
setup_warehouse_query: function() {
|
setup_warehouse_query: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
var warehouse_query_method = function() {
|
erpnext.queries.setup_queries(this.frm, "Warehouse", function() {
|
||||||
return erpnext.queries.warehouse(me.frm.doc);
|
return erpnext.queries.warehouse(me.frm.doc);
|
||||||
};
|
|
||||||
|
|
||||||
var _set_warehouse_query = function(doctype, parentfield) {
|
|
||||||
var warehouse_link_fields = frappe.meta.get_docfields(doctype, me.frm.doc.name,
|
|
||||||
{"fieldtype": "Link", "options": "Warehouse"});
|
|
||||||
$.each(warehouse_link_fields, function(i, df) {
|
|
||||||
if(parentfield) {
|
|
||||||
me.frm.set_query(df.fieldname, parentfield, warehouse_query_method);
|
|
||||||
} else {
|
|
||||||
me.frm.set_query(df.fieldname, warehouse_query_method);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_set_warehouse_query(me.frm.doc.doctype);
|
|
||||||
|
|
||||||
// warehouse field in tables
|
|
||||||
var table_fields = frappe.meta.get_docfields(me.frm.doc.doctype, me.frm.doc.name,
|
|
||||||
{"fieldtype": "Table"});
|
|
||||||
|
|
||||||
$.each(table_fields, function(i, df) {
|
|
||||||
_set_warehouse_query(df.options, df.fieldname);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -75,3 +75,26 @@ $.extend(erpnext.queries, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
erpnext.queries.setup_queries = function(frm, options, query_fn) {
|
||||||
|
var me = this;
|
||||||
|
var set_query = function(doctype, parentfield) {
|
||||||
|
var link_fields = frappe.meta.get_docfields(doctype, frm.doc.name,
|
||||||
|
{"fieldtype": "Link", "options": options});
|
||||||
|
$.each(link_fields, function(i, df) {
|
||||||
|
if(parentfield) {
|
||||||
|
frm.set_query(df.fieldname, parentfield, query_fn);
|
||||||
|
} else {
|
||||||
|
frm.set_query(df.fieldname, query_fn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
set_query(frm.doc.doctype);
|
||||||
|
|
||||||
|
// warehouse field in tables
|
||||||
|
$.each(frappe.meta.get_docfields(frm.doc.doctype, frm.doc.name, {"fieldtype": "Table"}),
|
||||||
|
function(i, df) {
|
||||||
|
set_query(df.options, df.fieldname);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ cur_frm.cscript.setup_dashboard = function(doc) {
|
|||||||
cur_frm.dashboard.add_doctype_badge("Sales Order", "customer");
|
cur_frm.dashboard.add_doctype_badge("Sales Order", "customer");
|
||||||
cur_frm.dashboard.add_doctype_badge("Delivery Note", "customer");
|
cur_frm.dashboard.add_doctype_badge("Delivery Note", "customer");
|
||||||
cur_frm.dashboard.add_doctype_badge("Sales Invoice", "customer");
|
cur_frm.dashboard.add_doctype_badge("Sales Invoice", "customer");
|
||||||
|
cur_frm.dashboard.add_doctype_badge("Project", "customer");
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -62,6 +63,8 @@ cur_frm.cscript.setup_dashboard = function(doc) {
|
|||||||
+ '</b> / <span class="text-muted">' + __("Unpaid") + ": <b>"
|
+ '</b> / <span class="text-muted">' + __("Unpaid") + ": <b>"
|
||||||
+ format_currency(r.message.total_unpaid, r.message["company_currency"][0])
|
+ format_currency(r.message.total_unpaid, r.message["company_currency"][0])
|
||||||
+ '</b></span>');
|
+ '</b></span>');
|
||||||
|
} else {
|
||||||
|
cur_frm.dashboard.set_headline("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur_frm.dashboard.set_badge_count(r.message);
|
cur_frm.dashboard.set_badge_count(r.message);
|
||||||
|
|||||||
@@ -1,35 +1,62 @@
|
|||||||
{
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-06-11 14:26:44",
|
"creation": "2013-06-11 14:26:44",
|
||||||
|
"custom": 0,
|
||||||
"description": "Buyer of Goods and Services.",
|
"description": "Buyer of Goods and Services.",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Master",
|
"document_type": "Master",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "basic_info",
|
"fieldname": "basic_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "",
|
"label": "",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "icon-user",
|
"options": "icon-user",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 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,
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Series",
|
"label": "Series",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "CUST-",
|
"options": "CUST-",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 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,
|
||||||
"fieldname": "customer_name",
|
"fieldname": "customer_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Full Name",
|
"label": "Full Name",
|
||||||
@@ -38,25 +65,43 @@
|
|||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "customer_type",
|
"fieldname": "customer_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "customer_type",
|
"oldfieldname": "customer_type",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nCompany\nIndividual",
|
"options": "\nCompany\nIndividual",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "lead_name",
|
"fieldname": "lead_name",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "From Lead",
|
"label": "From Lead",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "lead_name",
|
"oldfieldname": "lead_name",
|
||||||
@@ -64,228 +109,580 @@
|
|||||||
"options": "Lead",
|
"options": "Lead",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"report_hide": 1
|
"read_only": 0,
|
||||||
|
"report_hide": 1,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "column_break0",
|
"fieldname": "column_break0",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"description": "",
|
"description": "",
|
||||||
"fieldname": "customer_group",
|
"fieldname": "customer_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Customer Group",
|
"label": "Customer Group",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "customer_group",
|
"oldfieldname": "customer_group",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Customer Group",
|
"options": "Customer Group",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"description": "",
|
"description": "",
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Territory",
|
"label": "Territory",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "territory",
|
"oldfieldname": "territory",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Territory",
|
"options": "Territory",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "tax_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Tax ID",
|
||||||
|
"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,
|
||||||
"fieldname": "is_frozen",
|
"fieldname": "is_frozen",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Is Frozen",
|
"label": "Is Frozen",
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"depends_on": "eval:!doc.__islocal",
|
||||||
"fieldname": "address_contacts",
|
"fieldname": "address_contacts",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "",
|
"label": "",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "icon-map-marker",
|
"options": "icon-map-marker",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "address_html",
|
"fieldname": "address_html",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Address HTML",
|
"label": "Address HTML",
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "contact_html",
|
"fieldname": "contact_html",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Contact HTML",
|
"label": "Contact HTML",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldtype": "HTML",
|
"oldfieldtype": "HTML",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "default_receivable_accounts",
|
"fieldname": "default_receivable_accounts",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Default Receivable Accounts",
|
"label": "Default Receivable Accounts",
|
||||||
"permlevel": 0
|
"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,
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"depends_on": "eval:!doc.__islocal",
|
||||||
"description": "Mention if non-standard receivable account applicable",
|
"description": "Mention if non-standard receivable account applicable",
|
||||||
"fieldname": "accounts",
|
"fieldname": "accounts",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Accounts",
|
"label": "Accounts",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Party Account",
|
"options": "Party Account",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "more_info",
|
"fieldname": "more_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "",
|
"label": "",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "icon-file-text",
|
"options": "icon-file-text",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "column_break2",
|
"fieldname": "column_break2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Your Customer's TAX registration numbers (if applicable) or any general information",
|
"allow_on_submit": 0,
|
||||||
|
"description": "Additional information regarding the customer.",
|
||||||
"fieldname": "customer_details",
|
"fieldname": "customer_details",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Customer Details",
|
"label": "Customer Details",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "customer_details",
|
"oldfieldname": "customer_details",
|
||||||
"oldfieldtype": "Code",
|
"oldfieldtype": "Code",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "column_break3",
|
"fieldname": "column_break3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "default_currency",
|
"fieldname": "default_currency",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Currency",
|
"label": "Currency",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Currency",
|
"options": "Currency",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "default_price_list",
|
"fieldname": "default_price_list",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Price List",
|
"label": "Price List",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Price List",
|
"options": "Price List",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "default_taxes_and_charges",
|
"fieldname": "default_taxes_and_charges",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Taxes and Charges",
|
"label": "Taxes and Charges",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Sales Taxes and Charges Template",
|
"options": "Sales Taxes and Charges Template",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "credit_days_based_on",
|
"fieldname": "credit_days_based_on",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Credit Days Based On",
|
"label": "Credit Days Based On",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "\nFixed Days\nLast Day of the Next Month",
|
"options": "\nFixed Days\nLast Day of the Next Month",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
|
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
|
||||||
"fieldname": "credit_days",
|
"fieldname": "credit_days",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Credit Days",
|
"label": "Credit Days",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "credit_days",
|
"oldfieldname": "credit_days",
|
||||||
"oldfieldtype": "Int",
|
"oldfieldtype": "Int",
|
||||||
"permlevel": 1
|
"permlevel": 1,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "credit_limit",
|
"fieldname": "credit_limit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Credit Limit",
|
"label": "Credit Limit",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "credit_limit",
|
"oldfieldname": "credit_limit",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 1
|
"permlevel": 1,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "website",
|
"fieldname": "website",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Website",
|
"label": "Website",
|
||||||
"permlevel": 0
|
"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,
|
||||||
"fieldname": "sales_team_section_break",
|
"fieldname": "sales_team_section_break",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "",
|
"label": "",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "icon-group",
|
"options": "icon-group",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "default_sales_partner",
|
"fieldname": "default_sales_partner",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Sales Partner",
|
"label": "Sales Partner",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "default_sales_partner",
|
"oldfieldname": "default_sales_partner",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Sales Partner",
|
"options": "Sales Partner",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "default_commission_rate",
|
"fieldname": "default_commission_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Commission Rate",
|
"label": "Commission Rate",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "default_commission_rate",
|
"oldfieldname": "default_commission_rate",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "sales_team",
|
"fieldname": "sales_team",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Sales Team Details",
|
"label": "Sales Team Details",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "sales_team",
|
"oldfieldname": "sales_team",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Sales Team",
|
"options": "Sales Team",
|
||||||
"permlevel": 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,
|
||||||
"fieldname": "communications",
|
"fieldname": "communications",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Communications",
|
"label": "Communications",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Communication",
|
"options": "Communication",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
"icon": "icon-user",
|
"icon": "icon-user",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2015-07-17 09:38:50.086978",
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"modified": "2015-08-07 20:34:25.761769",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Customer",
|
"name": "Customer",
|
||||||
@@ -294,38 +691,73 @@
|
|||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales User",
|
"role": "Sales User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
|
"email": 0,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 1,
|
"permlevel": 1,
|
||||||
|
"print": 0,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Sales User"
|
"report": 0,
|
||||||
|
"role": "Sales User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales Manager"
|
"role": "Sales Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
@@ -337,46 +769,108 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
|
"email": 0,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 1,
|
"permlevel": 1,
|
||||||
|
"print": 0,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
"role": "Sales Master Manager",
|
"role": "Sales Master Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Stock User"
|
"role": "Stock User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Stock Manager"
|
"role": "Stock Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User"
|
"role": "Accounts User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 0,
|
||||||
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager"
|
"role": "Accounts Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
"search_fields": "customer_name,customer_group,territory",
|
"search_fields": "customer_name,customer_group,territory",
|
||||||
"title_field": "customer_name"
|
"title_field": "customer_name"
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,8 @@ def get_dashboard_info(customer):
|
|||||||
frappe.msgprint(_("Not permitted"), raise_exception=True)
|
frappe.msgprint(_("Not permitted"), raise_exception=True)
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
for doctype in ["Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
for doctype in ["Opportunity", "Quotation", "Sales Order", "Delivery Note",
|
||||||
|
"Sales Invoice", "Project"]:
|
||||||
out[doctype] = frappe.db.get_value(doctype,
|
out[doctype] = frappe.db.get_value(doctype,
|
||||||
{"customer": customer, "docstatus": ["!=", 2] }, "count(*)")
|
{"customer": customer, "docstatus": ["!=", 2] }, "count(*)")
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
{% include 'selling/sales_common.js' %}
|
{% include 'selling/sales_common.js' %}
|
||||||
|
|
||||||
|
frappe.ui.form.on("Sales Order", {
|
||||||
|
onload: function(frm) {
|
||||||
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
|
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
|
||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
this._super();
|
this._super();
|
||||||
@@ -16,29 +24,32 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
||||||
// doc.per_billed);
|
// doc.per_billed);
|
||||||
|
|
||||||
// delivery note
|
|
||||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
|
||||||
cur_frm.add_custom_button(__('Make Delivery'), this.make_delivery_note);
|
|
||||||
|
|
||||||
// indent
|
// indent
|
||||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||||
cur_frm.add_custom_button(__('Make ') + __('Material Request'),
|
cur_frm.add_custom_button(__('Material Request'), this.make_material_request);
|
||||||
this.make_material_request);
|
|
||||||
|
|
||||||
// sales invoice
|
if(flt(doc.per_billed)==0) {
|
||||||
if(flt(doc.per_billed, 2) < 100) {
|
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop
|
// stop
|
||||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'])
|
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop 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) {
|
||||||
cur_frm.add_custom_button(__('Make Maint. Visit'), this.make_maintenance_visit);
|
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
|
||||||
cur_frm.add_custom_button(__('Make Maint. Schedule'), this.make_maintenance_schedule);
|
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delivery note
|
||||||
|
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||||
|
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
|
||||||
|
|
||||||
|
// sales invoice
|
||||||
|
if(flt(doc.per_billed, 2) < 100) {
|
||||||
|
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// un-stop
|
// un-stop
|
||||||
@@ -122,6 +133,20 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
make_bank_entry: function() {
|
||||||
|
return frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_order",
|
||||||
|
args: {
|
||||||
|
"sales_order": cur_frm.doc.name
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// for backward compatibility: combine new and previous states
|
// for backward compatibility: combine new and previous states
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class Company(Document):
|
|||||||
frappe.throw(_("Abbreviation cannot have more than 5 characters"))
|
frappe.throw(_("Abbreviation cannot have more than 5 characters"))
|
||||||
|
|
||||||
if not self.abbr.strip():
|
if not self.abbr.strip():
|
||||||
frappe.throw(_("Abbr can not be blank or space"))
|
frappe.throw(_("Abbreviation is mandatory"))
|
||||||
|
|
||||||
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
|
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
|
||||||
if self.default_currency and self.previous_default_currency and \
|
if self.default_currency and self.previous_default_currency and \
|
||||||
|
|||||||
@@ -14,22 +14,22 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
|||||||
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
|
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
|
||||||
var from_sales_invoice = false;
|
var from_sales_invoice = false;
|
||||||
from_sales_invoice = cur_frm.doc.items.some(function(item) {
|
from_sales_invoice = cur_frm.doc.items.some(function(item) {
|
||||||
return item.against_sales_invoice ? true : false;
|
return item.against_sales_invoice ? true : false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!from_sales_invoice)
|
if(!from_sales_invoice)
|
||||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice);
|
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
|
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
|
||||||
cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note);
|
cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note);
|
||||||
|
|
||||||
if (doc.docstatus==1) {
|
if (doc.docstatus==1) {
|
||||||
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return);
|
cur_frm.add_custom_button(__('Sales Return'), this.make_sales_return);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus==0 && !doc.__islocal) {
|
if(doc.docstatus==0 && !doc.__islocal) {
|
||||||
cur_frm.add_custom_button(__('Make Packing Slip'),
|
cur_frm.add_custom_button(__('Packing Slip'),
|
||||||
cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]);
|
cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 0)
|
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 0)
|
||||||
|
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=5, incoming_rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
|
||||||
|
|
||||||
stock_queue = json.loads(get_previous_sle({
|
stock_queue = json.loads(get_previous_sle({
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
@@ -59,7 +59,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
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)
|
||||||
frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO")
|
frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO")
|
||||||
|
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=5, incoming_rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
||||||
prev_bal = get_balance_on(stock_in_hand_account)
|
prev_bal = get_balance_on(stock_in_hand_account)
|
||||||
@@ -85,7 +85,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
# back dated incoming entry
|
# back dated incoming entry
|
||||||
make_stock_entry(posting_date=add_days(nowdate(), -2), target="_Test Warehouse - _TC",
|
make_stock_entry(posting_date=add_days(nowdate(), -2), target="_Test Warehouse - _TC",
|
||||||
qty=5, incoming_rate=100)
|
qty=5, basic_rate=100)
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
@@ -107,9 +107,9 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
def test_delivery_note_gl_entry_packing_item(self):
|
def test_delivery_note_gl_entry_packing_item(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||||
make_stock_entry(item_code="_Test Item Home Desktop 100",
|
make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||||
target="_Test Warehouse - _TC", qty=10, incoming_rate=100)
|
target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
||||||
prev_bal = get_balance_on(stock_in_hand_account)
|
prev_bal = get_balance_on(stock_in_hand_account)
|
||||||
@@ -184,7 +184,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
def test_sales_return_for_non_bundled_items(self):
|
def test_sales_return_for_non_bundled_items(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||||
|
|
||||||
actual_qty_0 = get_qty_after_transaction()
|
actual_qty_0 = get_qty_after_transaction()
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.website_image = self.image
|
self.website_image = self.image
|
||||||
|
|
||||||
self.check_warehouse_is_set_for_stock_item()
|
self.check_warehouse_is_set_for_stock_item()
|
||||||
self.check_stock_uom_with_bin()
|
self.validate_uom()
|
||||||
self.add_default_uom_in_conversion_factor_table()
|
self.add_default_uom_in_conversion_factor_table()
|
||||||
self.validate_conversion_factor()
|
self.validate_conversion_factor()
|
||||||
self.validate_item_type()
|
self.validate_item_type()
|
||||||
@@ -105,35 +105,6 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
[self.remove(d) for d in to_remove]
|
[self.remove(d) for d in to_remove]
|
||||||
|
|
||||||
|
|
||||||
def check_stock_uom_with_bin(self):
|
|
||||||
if not self.get("__islocal"):
|
|
||||||
if self.stock_uom == frappe.db.get_value("Item", self.name, "stock_uom"):
|
|
||||||
return
|
|
||||||
|
|
||||||
matched=True
|
|
||||||
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
|
||||||
{"item_code": self.name}, "stock_uom")
|
|
||||||
|
|
||||||
if ref_uom:
|
|
||||||
if cstr(ref_uom) != cstr(self.stock_uom):
|
|
||||||
matched = False
|
|
||||||
else:
|
|
||||||
bin_list = frappe.db.sql("select * from tabBin where item_code=%s",
|
|
||||||
self.item_code, as_dict=1)
|
|
||||||
for bin in bin_list:
|
|
||||||
if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
|
|
||||||
or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(self.stock_uom):
|
|
||||||
matched = False
|
|
||||||
break
|
|
||||||
|
|
||||||
if matched and bin_list:
|
|
||||||
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""",
|
|
||||||
(self.stock_uom, self.name))
|
|
||||||
|
|
||||||
if not matched:
|
|
||||||
frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module.").format(self.name))
|
|
||||||
|
|
||||||
def update_template_tables(self):
|
def update_template_tables(self):
|
||||||
template = frappe.get_doc("Item", self.variant_of)
|
template = frappe.get_doc("Item", self.variant_of)
|
||||||
|
|
||||||
@@ -345,6 +316,17 @@ class Item(WebsiteGenerator):
|
|||||||
if stock_in:
|
if stock_in:
|
||||||
frappe.throw(_("Item Template cannot have stock or Open Sales/Purchase/Production Orders."), ItemTemplateCannotHaveStock)
|
frappe.throw(_("Item Template cannot have stock or Open Sales/Purchase/Production Orders."), ItemTemplateCannotHaveStock)
|
||||||
|
|
||||||
|
def validate_uom(self):
|
||||||
|
if not self.get("__islocal"):
|
||||||
|
check_stock_uom_with_bin(self.name, self.stock_uom)
|
||||||
|
if self.has_variants:
|
||||||
|
for d in frappe.db.get_all("Item", filters= {"variant_of": self.name}):
|
||||||
|
check_stock_uom_with_bin(d.name, self.stock_uom)
|
||||||
|
if self.variant_of:
|
||||||
|
template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
|
||||||
|
if template_uom != self.stock_uom:
|
||||||
|
frappe.throw(_("Default Unit of Measure for Variant must be same as Template"))
|
||||||
|
|
||||||
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
|
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
|
||||||
if not end_of_life:
|
if not end_of_life:
|
||||||
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")
|
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")
|
||||||
@@ -449,3 +431,30 @@ def invalidate_cache_for_item(doc):
|
|||||||
|
|
||||||
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
|
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
|
||||||
invalidate_cache_for(doc, doc.old_item_group)
|
invalidate_cache_for(doc, doc.old_item_group)
|
||||||
|
|
||||||
|
def check_stock_uom_with_bin(item, stock_uom):
|
||||||
|
if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
|
||||||
|
return
|
||||||
|
|
||||||
|
matched=True
|
||||||
|
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
||||||
|
{"item_code": item}, "stock_uom")
|
||||||
|
|
||||||
|
if ref_uom:
|
||||||
|
if cstr(ref_uom) != cstr(stock_uom):
|
||||||
|
matched = False
|
||||||
|
else:
|
||||||
|
bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
|
||||||
|
for bin in bin_list:
|
||||||
|
if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
|
||||||
|
or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
|
||||||
|
matched = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if matched and bin_list:
|
||||||
|
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
|
||||||
|
|
||||||
|
if not matched:
|
||||||
|
frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because \
|
||||||
|
you have already made some transaction(s) with another UOM. To change default UOM, \
|
||||||
|
use 'UOM Replace Utility' tool under Stock module.").format(item))
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class TestItem(unittest.TestCase):
|
|||||||
|
|
||||||
def test_template_cannot_have_stock(self):
|
def test_template_cannot_have_stock(self):
|
||||||
item = self.get_item(10)
|
item = self.get_item(10)
|
||||||
make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, incoming_rate=1)
|
make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, basic_rate=1)
|
||||||
item.has_variants = 1
|
item.has_variants = 1
|
||||||
self.assertRaises(ItemTemplateCannotHaveStock, item.save)
|
self.assertRaises(ItemTemplateCannotHaveStock, item.save)
|
||||||
|
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ def make_stock_entry(source_name, target_doc=None):
|
|||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.purpose = source.material_request_type
|
target.purpose = source.material_request_type
|
||||||
target.run_method("get_stock_and_rate")
|
target.run_method("calculate_rate_and_amount")
|
||||||
|
|
||||||
doclist = get_mapped_doc("Material Request", source_name, {
|
doclist = get_mapped_doc("Material Request", source_name, {
|
||||||
"Material Request": {
|
"Material Request": {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"item_code": "_Test Item Home Desktop 100",
|
"item_code": "_Test Item Home Desktop 100",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"qty": qty1,
|
"qty": qty1,
|
||||||
"stock_uom": "_Test UOM 1",
|
"stock_uom": "_Test UOM 1",
|
||||||
"transfer_qty": qty1,
|
"transfer_qty": qty1,
|
||||||
@@ -84,7 +84,7 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"item_code": "_Test Item Home Desktop 200",
|
"item_code": "_Test Item Home Desktop 200",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"qty": qty2,
|
"qty": qty2,
|
||||||
"stock_uom": "_Test UOM 1",
|
"stock_uom": "_Test UOM 1",
|
||||||
"transfer_qty": qty2,
|
"transfer_qty": qty2,
|
||||||
@@ -196,13 +196,13 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
"qty": 27.0,
|
"qty": 27.0,
|
||||||
"transfer_qty": 27.0,
|
"transfer_qty": 27.0,
|
||||||
"s_warehouse": "_Test Warehouse 1 - _TC",
|
"s_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
se_doc.get("items")[1].update({
|
se_doc.get("items")[1].update({
|
||||||
"qty": 1.5,
|
"qty": 1.5,
|
||||||
"transfer_qty": 1.5,
|
"transfer_qty": 1.5,
|
||||||
"s_warehouse": "_Test Warehouse 1 - _TC",
|
"s_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
|
|
||||||
# make available the qty in _Test Warehouse 1 before transfer
|
# make available the qty in _Test Warehouse 1 before transfer
|
||||||
@@ -279,13 +279,13 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
"qty": 60.0,
|
"qty": 60.0,
|
||||||
"transfer_qty": 60.0,
|
"transfer_qty": 60.0,
|
||||||
"s_warehouse": "_Test Warehouse 1 - _TC",
|
"s_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
se_doc.get("items")[1].update({
|
se_doc.get("items")[1].update({
|
||||||
"qty": 3.0,
|
"qty": 3.0,
|
||||||
"transfer_qty": 3.0,
|
"transfer_qty": 3.0,
|
||||||
"s_warehouse": "_Test Warehouse 1 - _TC",
|
"s_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
|
|
||||||
# make available the qty in _Test Warehouse 1 before transfer
|
# make available the qty in _Test Warehouse 1 before transfer
|
||||||
@@ -350,13 +350,13 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
"transfer_qty": 60.0,
|
"transfer_qty": 60.0,
|
||||||
"s_warehouse": "_Test Warehouse - _TC",
|
"s_warehouse": "_Test Warehouse - _TC",
|
||||||
"t_warehouse": "_Test Warehouse 1 - _TC",
|
"t_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
se_doc.get("items")[1].update({
|
se_doc.get("items")[1].update({
|
||||||
"qty": 3.0,
|
"qty": 3.0,
|
||||||
"transfer_qty": 3.0,
|
"transfer_qty": 3.0,
|
||||||
"s_warehouse": "_Test Warehouse 1 - _TC",
|
"s_warehouse": "_Test Warehouse 1 - _TC",
|
||||||
"incoming_rate": 1.0
|
"basic_rate": 1.0
|
||||||
})
|
})
|
||||||
|
|
||||||
# check for stopped status of Material Request
|
# check for stopped status of Material Request
|
||||||
|
|||||||
@@ -52,11 +52,10 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(this.frm.doc.docstatus == 1) {
|
if(this.frm.doc.docstatus == 1) {
|
||||||
|
cur_frm.add_custom_button(__('Return'), this.make_purchase_return);
|
||||||
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
|
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
|
||||||
cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice);
|
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
def test_subcontracting(self):
|
def test_subcontracting(self):
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
|
||||||
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
|
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
|
||||||
qty=100, incoming_rate=100)
|
qty=100, basic_rate=100)
|
||||||
|
|
||||||
pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
|
pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
|
||||||
self.assertEquals(len(pr.get("supplied_items")), 2)
|
self.assertEquals(len(pr.get("supplied_items")), 2)
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
|
|
||||||
this.frm.fields_dict.bom_no.get_query = function() {
|
this.frm.fields_dict.bom_no.get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{ 'docstatus': 1 }
|
filters:{
|
||||||
|
"docstatus": 1,
|
||||||
|
"is_active": 1
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,7 +25,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"is_subcontracted": "Yes"
|
"is_subcontracted": "Yes",
|
||||||
|
"company": me.frm.doc.company
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -38,6 +42,14 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.frm.set_query("difference_account", function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"company": me.frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -122,11 +134,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
var d = locals[cdt][cdn];
|
var d = locals[cdt][cdn];
|
||||||
d.transfer_qty = flt(d.qty) * flt(d.conversion_factor);
|
d.transfer_qty = flt(d.qty) * flt(d.conversion_factor);
|
||||||
refresh_field('items');
|
refresh_field('items');
|
||||||
calculate_total(doc, cdt, cdn);
|
|
||||||
},
|
|
||||||
|
|
||||||
incoming_rate: function(doc, cdt, cdn) {
|
|
||||||
calculate_total(doc, cdt, cdn);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
production_order: function() {
|
production_order: function() {
|
||||||
@@ -135,13 +142,29 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_production_order_details",
|
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_production_order_details",
|
||||||
args: {production_order: this.frm.doc.production_order},
|
args: {production_order: me.frm.doc.production_order},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
me.frm.set_value(r.message);
|
$.each(["from_bom", "bom_no", "fg_completed_qty", "use_multi_level_bom"], function(i, field) {
|
||||||
|
me.frm.set_value(field, r.message[field]);
|
||||||
|
})
|
||||||
|
|
||||||
if (me.frm.doc.purpose == "Material Transfer for Manufacture" && !me.frm.doc.to_warehouse)
|
if (me.frm.doc.purpose == "Material Transfer for Manufacture" && !me.frm.doc.to_warehouse)
|
||||||
me.frm.set_value("to_warehouse", r.message["wip_warehouse"]);
|
me.frm.set_value("to_warehouse", r.message["wip_warehouse"]);
|
||||||
me.frm.set_value("from_bom", 1);
|
|
||||||
|
|
||||||
|
if (me.frm.doc.purpose == "Manufacture") {
|
||||||
|
if(r.message["additional_costs"].length) {
|
||||||
|
$.each(r.message["additional_costs"], function(i, row) {
|
||||||
|
me.frm.add_child("additional_costs", row);
|
||||||
|
})
|
||||||
|
refresh_field("additional_costs");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!me.frm.doc.from_warehouse) me.frm.set_value("from_warehouse", r.message["wip_warehouse"]);
|
||||||
|
if (!me.frm.doc.to_warehouse) me.frm.set_value("to_warehouse", r.message["fg_warehouse"]);
|
||||||
|
}
|
||||||
|
me.get_items()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -229,13 +252,20 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
|
|||||||
if(doc.purpose == "Material Receipt") {
|
if(doc.purpose == "Material Receipt") {
|
||||||
cur_frm.set_value("from_bom", 0);
|
cur_frm.set_value("from_bom", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Addition costs based on purpose
|
||||||
|
cur_frm.toggle_display(["additional_costs", "total_additional_costs", "additional_costs_section"],
|
||||||
|
doc.purpose!='Material Issue');
|
||||||
|
|
||||||
|
cur_frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue');
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.fields_dict['production_order'].get_query = function(doc) {
|
cur_frm.fields_dict['production_order'].get_query = function(doc) {
|
||||||
return {
|
return {
|
||||||
filters: [
|
filters: [
|
||||||
['Production Order', 'docstatus', '=', 1],
|
['Production Order', 'docstatus', '=', 1],
|
||||||
['Production Order', 'qty', '>','`tabProduction Order`.produced_qty']
|
['Production Order', 'qty', '>','`tabProduction Order`.produced_qty'],
|
||||||
|
['Production Order', 'company', '=', cur_frm.doc.company]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,16 +406,3 @@ 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){
|
||||||
erpnext.get_fiscal_year(doc.company, doc.posting_date);
|
erpnext.get_fiscal_year(doc.company, doc.posting_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
var calculate_total = function(doc, cdt, cdn){
|
|
||||||
var d = locals[cdt][cdn];
|
|
||||||
amount = flt(d.incoming_rate) * flt(d.transfer_qty)
|
|
||||||
frappe.model.set_value(cdt, cdn, 'amount', amount);
|
|
||||||
var total_amount = 0.0;
|
|
||||||
var items = doc.items || [];
|
|
||||||
for(var i=0;i<items.length;i++) {
|
|
||||||
total_amount += flt(items[i].amount);
|
|
||||||
}
|
|
||||||
doc.total_amount = total_amount;
|
|
||||||
refresh_field("total_amount");
|
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -29,8 +29,7 @@ class StockEntry(StockController):
|
|||||||
def onload(self):
|
def onload(self):
|
||||||
if self.docstatus==1:
|
if self.docstatus==1:
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
item.update(get_available_qty(item.item_code,
|
item.update(get_available_qty(item.item_code, item.s_warehouse))
|
||||||
item.s_warehouse))
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.pro_doc = None
|
self.pro_doc = None
|
||||||
@@ -46,15 +45,14 @@ class StockEntry(StockController):
|
|||||||
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
self.get_stock_and_rate()
|
|
||||||
self.validate_bom()
|
self.validate_bom()
|
||||||
self.validate_finished_goods()
|
self.validate_finished_goods()
|
||||||
self.validate_with_material_request()
|
self.validate_with_material_request()
|
||||||
self.validate_valuation_rate()
|
|
||||||
self.set_total_incoming_outgoing_value()
|
|
||||||
self.set_total_amount()
|
|
||||||
self.validate_batch()
|
self.validate_batch()
|
||||||
|
|
||||||
|
self.set_actual_qty()
|
||||||
|
self.calculate_rate_and_amount()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
@@ -100,7 +98,7 @@ class StockEntry(StockController):
|
|||||||
if f in ["stock_uom", "conversion_factor"] or not item.get(f):
|
if f in ["stock_uom", "conversion_factor"] or not item.get(f):
|
||||||
item.set(f, item_details.get(f))
|
item.set(f, item_details.get(f))
|
||||||
|
|
||||||
if self.difference_account:
|
if self.difference_account and not item.expense_account:
|
||||||
item.expense_account = self.difference_account
|
item.expense_account = self.difference_account
|
||||||
|
|
||||||
if not item.transfer_qty:
|
if not item.transfer_qty:
|
||||||
@@ -214,6 +212,88 @@ class StockEntry(StockController):
|
|||||||
frappe.throw(_("Stock Entries already created for Production Order ")
|
frappe.throw(_("Stock Entries already created for Production Order ")
|
||||||
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
||||||
|
|
||||||
|
def set_actual_qty(self):
|
||||||
|
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
||||||
|
|
||||||
|
for d in self.get('items'):
|
||||||
|
previous_sle = get_previous_sle({
|
||||||
|
"item_code": d.item_code,
|
||||||
|
"warehouse": d.s_warehouse or d.t_warehouse,
|
||||||
|
"posting_date": self.posting_date,
|
||||||
|
"posting_time": self.posting_time
|
||||||
|
})
|
||||||
|
|
||||||
|
# get actual stock at source warehouse
|
||||||
|
d.actual_qty = previous_sle.get("qty_after_transaction") or 0
|
||||||
|
|
||||||
|
# validate qty during submit
|
||||||
|
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
|
||||||
|
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
|
||||||
|
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
|
||||||
|
self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
|
||||||
|
|
||||||
|
def calculate_rate_and_amount(self, force=False):
|
||||||
|
self.set_basic_rate(force)
|
||||||
|
self.distribute_additional_costs()
|
||||||
|
self.update_valuation_rate()
|
||||||
|
self.validate_valuation_rate()
|
||||||
|
self.set_total_incoming_outgoing_value()
|
||||||
|
self.set_total_amount()
|
||||||
|
|
||||||
|
def set_basic_rate(self, force=False):
|
||||||
|
"""get stock and incoming rate on posting date"""
|
||||||
|
raw_material_cost = 0.0
|
||||||
|
|
||||||
|
for d in self.get('items'):
|
||||||
|
args = frappe._dict({
|
||||||
|
"item_code": d.item_code,
|
||||||
|
"warehouse": d.s_warehouse or d.t_warehouse,
|
||||||
|
"posting_date": self.posting_date,
|
||||||
|
"posting_time": self.posting_time,
|
||||||
|
"qty": d.s_warehouse and -1*flt(d.transfer_qty) or flt(d.transfer_qty),
|
||||||
|
"serial_no": d.serial_no,
|
||||||
|
})
|
||||||
|
|
||||||
|
# get basic rate
|
||||||
|
if not d.bom_no:
|
||||||
|
if not flt(d.basic_rate) or d.s_warehouse or force:
|
||||||
|
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
|
||||||
|
if basic_rate > 0:
|
||||||
|
d.basic_rate = basic_rate
|
||||||
|
|
||||||
|
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
||||||
|
if not d.t_warehouse:
|
||||||
|
raw_material_cost += flt(d.basic_amount)
|
||||||
|
|
||||||
|
self.set_basic_rate_for_finished_goods(raw_material_cost)
|
||||||
|
|
||||||
|
def set_basic_rate_for_finished_goods(self, raw_material_cost):
|
||||||
|
if self.purpose in ["Manufacture", "Repack"]:
|
||||||
|
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
||||||
|
for d in self.get("items"):
|
||||||
|
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||||
|
d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate"))
|
||||||
|
d.basic_amount = flt(flt(d.basic_rate) * flt(d.transfer_qty), d.precision("basic_amount"))
|
||||||
|
|
||||||
|
def distribute_additional_costs(self):
|
||||||
|
if self.purpose == "Material Issue":
|
||||||
|
self.additional_costs = []
|
||||||
|
|
||||||
|
self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
|
||||||
|
total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
|
||||||
|
|
||||||
|
for d in self.get("items"):
|
||||||
|
if d.t_warehouse and total_basic_amount:
|
||||||
|
d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs
|
||||||
|
else:
|
||||||
|
d.additional_cost = 0
|
||||||
|
|
||||||
|
def update_valuation_rate(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount"))
|
||||||
|
d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty),
|
||||||
|
d.precision("valuation_rate"))
|
||||||
|
|
||||||
def validate_valuation_rate(self):
|
def validate_valuation_rate(self):
|
||||||
if self.purpose in ["Manufacture", "Repack"]:
|
if self.purpose in ["Manufacture", "Repack"]:
|
||||||
valuation_at_source, valuation_at_target = 0, 0
|
valuation_at_source, valuation_at_target = 0, 0
|
||||||
@@ -224,100 +304,22 @@ class StockEntry(StockController):
|
|||||||
valuation_at_target += flt(d.amount)
|
valuation_at_target += flt(d.amount)
|
||||||
|
|
||||||
if valuation_at_target + 0.001 < valuation_at_source:
|
if valuation_at_target + 0.001 < valuation_at_source:
|
||||||
frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})").format(valuation_at_target,
|
frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})")
|
||||||
valuation_at_source))
|
.format(valuation_at_target, valuation_at_source))
|
||||||
|
|
||||||
def set_total_incoming_outgoing_value(self):
|
def set_total_incoming_outgoing_value(self):
|
||||||
self.total_incoming_value = self.total_outgoing_value = 0.0
|
self.total_incoming_value = self.total_outgoing_value = 0.0
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.s_warehouse:
|
|
||||||
self.total_incoming_value += flt(d.amount)
|
|
||||||
if d.t_warehouse:
|
if d.t_warehouse:
|
||||||
|
self.total_incoming_value += flt(d.amount)
|
||||||
|
if d.s_warehouse:
|
||||||
self.total_outgoing_value += flt(d.amount)
|
self.total_outgoing_value += flt(d.amount)
|
||||||
|
|
||||||
self.value_difference = self.total_outgoing_value - self.total_incoming_value
|
self.value_difference = self.total_incoming_value - self.total_outgoing_value
|
||||||
|
|
||||||
def set_total_amount(self):
|
def set_total_amount(self):
|
||||||
self.total_amount = sum([flt(item.amount) for item in self.get("items")])
|
self.total_amount = sum([flt(item.amount) for item in self.get("items")])
|
||||||
|
|
||||||
def get_stock_and_rate(self, force=False):
|
|
||||||
"""get stock and incoming rate on posting date"""
|
|
||||||
|
|
||||||
raw_material_cost = 0.0
|
|
||||||
|
|
||||||
if not self.posting_date or not self.posting_time:
|
|
||||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
|
||||||
|
|
||||||
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
|
||||||
|
|
||||||
for d in self.get('items'):
|
|
||||||
d.transfer_qty = flt(d.transfer_qty)
|
|
||||||
|
|
||||||
args = frappe._dict({
|
|
||||||
"item_code": d.item_code,
|
|
||||||
"warehouse": d.s_warehouse or d.t_warehouse,
|
|
||||||
"posting_date": self.posting_date,
|
|
||||||
"posting_time": self.posting_time,
|
|
||||||
"qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty,
|
|
||||||
"serial_no": d.serial_no,
|
|
||||||
})
|
|
||||||
|
|
||||||
# get actual stock at source warehouse
|
|
||||||
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
|
|
||||||
|
|
||||||
# validate qty during submit
|
|
||||||
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
|
|
||||||
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
|
|
||||||
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
|
|
||||||
self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
|
|
||||||
|
|
||||||
# get incoming rate
|
|
||||||
if not d.bom_no:
|
|
||||||
if not flt(d.incoming_rate) or d.s_warehouse or force:
|
|
||||||
incoming_rate = flt(get_incoming_rate(args), self.precision("incoming_rate", d))
|
|
||||||
if incoming_rate > 0:
|
|
||||||
d.incoming_rate = incoming_rate
|
|
||||||
|
|
||||||
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), d.precision("amount"))
|
|
||||||
if not d.t_warehouse:
|
|
||||||
raw_material_cost += flt(d.amount)
|
|
||||||
|
|
||||||
|
|
||||||
self.add_operation_cost(raw_material_cost, force)
|
|
||||||
|
|
||||||
def add_operation_cost(self, raw_material_cost, force):
|
|
||||||
"""Adds operating cost if Production Order is set"""
|
|
||||||
# set incoming rate for fg item
|
|
||||||
if self.purpose in ["Manufacture", "Repack"]:
|
|
||||||
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
|
||||||
for d in self.get("items"):
|
|
||||||
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
|
||||||
operation_cost_per_unit = self.get_operation_cost_per_unit(d.bom_no, d.qty)
|
|
||||||
|
|
||||||
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
|
|
||||||
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d))
|
|
||||||
break
|
|
||||||
|
|
||||||
def get_operation_cost_per_unit(self, bom_no, qty):
|
|
||||||
"""Returns operating cost from Production Order for given `bom_no`"""
|
|
||||||
operation_cost_per_unit = 0
|
|
||||||
|
|
||||||
if self.production_order:
|
|
||||||
if not getattr(self, "pro_doc", None):
|
|
||||||
self.pro_doc = frappe.get_doc("Production Order", self.production_order)
|
|
||||||
for d in self.pro_doc.get("operations"):
|
|
||||||
if flt(d.completed_qty):
|
|
||||||
operation_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
|
||||||
else:
|
|
||||||
operation_cost_per_unit += flt(d.planned_operating_cost) / flt(self.pro_doc.qty)
|
|
||||||
|
|
||||||
# set operating cost from BOM if specified.
|
|
||||||
if not operation_cost_per_unit and bom_no:
|
|
||||||
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
|
||||||
operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
|
||||||
|
|
||||||
return operation_cost_per_unit + (flt(self.additional_operating_cost) / flt(qty))
|
|
||||||
|
|
||||||
def validate_purchase_order(self):
|
def validate_purchase_order(self):
|
||||||
"""Throw exception if more raw material is transferred against Purchase Order than in
|
"""Throw exception if more raw material is transferred against Purchase Order than in
|
||||||
the raw materials supplied table"""
|
the raw materials supplied table"""
|
||||||
@@ -378,7 +380,7 @@ class StockEntry(StockController):
|
|||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
"warehouse": cstr(d.t_warehouse),
|
"warehouse": cstr(d.t_warehouse),
|
||||||
"actual_qty": flt(d.transfer_qty),
|
"actual_qty": flt(d.transfer_qty),
|
||||||
"incoming_rate": flt(d.incoming_rate)
|
"incoming_rate": flt(d.valuation_rate)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
# On cancellation, make stock ledger entry for
|
# On cancellation, make stock ledger entry for
|
||||||
@@ -393,6 +395,32 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
|
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
|
||||||
|
|
||||||
|
def get_gl_entries(self, warehouse_account):
|
||||||
|
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||||
|
|
||||||
|
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
|
||||||
|
|
||||||
|
for d in self.get("items"):
|
||||||
|
additional_cost = flt(d.additional_cost, d.precision("additional_cost"))
|
||||||
|
if additional_cost:
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": expenses_included_in_valuation,
|
||||||
|
"against": d.expense_account,
|
||||||
|
"cost_center": d.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": additional_cost
|
||||||
|
}))
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": d.expense_account,
|
||||||
|
"against": expenses_included_in_valuation,
|
||||||
|
"cost_center": d.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": -1 * additional_cost # put it as negative credit instead of debit purposefully
|
||||||
|
}))
|
||||||
|
|
||||||
|
return gl_entries
|
||||||
|
|
||||||
def update_production_order(self):
|
def update_production_order(self):
|
||||||
def _validate_production_order(pro_doc):
|
def _validate_production_order(pro_doc):
|
||||||
if flt(pro_doc.docstatus) != 1:
|
if flt(pro_doc.docstatus) != 1:
|
||||||
@@ -442,7 +470,7 @@ class StockEntry(StockController):
|
|||||||
'conversion_factor' : 1,
|
'conversion_factor' : 1,
|
||||||
'batch_no' : '',
|
'batch_no' : '',
|
||||||
'actual_qty' : 0,
|
'actual_qty' : 0,
|
||||||
'incoming_rate' : 0
|
'basic_rate' : 0
|
||||||
}
|
}
|
||||||
for d in [["Account", "expense_account", "default_expense_account"],
|
for d in [["Account", "expense_account", "default_expense_account"],
|
||||||
["Cost Center", "cost_center", "cost_center"]]:
|
["Cost Center", "cost_center", "cost_center"]]:
|
||||||
@@ -490,7 +518,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
|
"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
|
||||||
"incoming_rate" : get_incoming_rate(args)
|
"basic_rate" : get_incoming_rate(args)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -498,6 +526,9 @@ class StockEntry(StockController):
|
|||||||
self.set('items', [])
|
self.set('items', [])
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
|
|
||||||
|
if not self.posting_date or not self.posting_time:
|
||||||
|
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||||
|
|
||||||
if not getattr(self, "pro_doc", None):
|
if not getattr(self, "pro_doc", None):
|
||||||
self.pro_doc = None
|
self.pro_doc = None
|
||||||
|
|
||||||
@@ -520,6 +551,12 @@ class StockEntry(StockController):
|
|||||||
if self.to_warehouse and self.pro_doc:
|
if self.to_warehouse and self.pro_doc:
|
||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
||||||
|
self.add_to_stock_entry_detail(item_dict)
|
||||||
|
|
||||||
|
elif self.production_order and self.purpose == "Manufacture" and \
|
||||||
|
frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture":
|
||||||
|
self.get_transfered_raw_materials()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not self.fg_completed_qty:
|
if not self.fg_completed_qty:
|
||||||
frappe.throw(_("Manufacturing Quantity is mandatory"))
|
frappe.throw(_("Manufacturing Quantity is mandatory"))
|
||||||
@@ -530,15 +567,14 @@ class StockEntry(StockController):
|
|||||||
item["from_warehouse"] = self.pro_doc.wip_warehouse
|
item["from_warehouse"] = self.pro_doc.wip_warehouse
|
||||||
|
|
||||||
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
|
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
|
||||||
|
self.add_to_stock_entry_detail(item_dict)
|
||||||
# add raw materials to Stock Entry Detail table
|
|
||||||
self.add_to_stock_entry_detail(item_dict)
|
|
||||||
|
|
||||||
# add finished goods item
|
# add finished goods item
|
||||||
if self.purpose in ("Manufacture", "Repack"):
|
if self.purpose in ("Manufacture", "Repack"):
|
||||||
self.load_items_from_bom()
|
self.load_items_from_bom()
|
||||||
|
|
||||||
self.get_stock_and_rate()
|
self.set_actual_qty()
|
||||||
|
self.calculate_rate_and_amount()
|
||||||
|
|
||||||
def load_items_from_bom(self):
|
def load_items_from_bom(self):
|
||||||
if self.production_order:
|
if self.production_order:
|
||||||
@@ -568,13 +604,71 @@ class StockEntry(StockController):
|
|||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||||
|
|
||||||
# item dict = { item_code: {qty, description, stock_uom} }
|
# item dict = { item_code: {qty, description, stock_uom} }
|
||||||
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom)
|
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
|
||||||
|
fetch_exploded = self.use_multi_level_bom)
|
||||||
|
|
||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
item.from_warehouse = self.from_warehouse or item.default_warehouse
|
item.from_warehouse = self.from_warehouse or item.default_warehouse
|
||||||
|
|
||||||
return item_dict
|
return item_dict
|
||||||
|
|
||||||
|
def get_transfered_raw_materials(self):
|
||||||
|
transferred_materials = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
||||||
|
description, stock_uom, expense_account, cost_center
|
||||||
|
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
||||||
|
where
|
||||||
|
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
||||||
|
and se.production_order= %s and ifnull(sed.t_warehouse, '') != ''
|
||||||
|
group by sed.item_code, sed.t_warehouse
|
||||||
|
""", self.production_order, as_dict=1)
|
||||||
|
|
||||||
|
materials_already_backflushed = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
item_code, sed.s_warehouse as warehouse, sum(qty) as qty
|
||||||
|
from
|
||||||
|
`tabStock Entry` se, `tabStock Entry Detail` sed
|
||||||
|
where
|
||||||
|
se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture'
|
||||||
|
and se.production_order= %s and ifnull(sed.s_warehouse, '') != ''
|
||||||
|
group by sed.item_code, sed.s_warehouse
|
||||||
|
""", self.production_order, as_dict=1)
|
||||||
|
|
||||||
|
backflushed_materials= {}
|
||||||
|
for d in materials_already_backflushed:
|
||||||
|
backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty})
|
||||||
|
|
||||||
|
po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from
|
||||||
|
`tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0]
|
||||||
|
manufacturing_qty = flt(po_qty.qty)
|
||||||
|
produced_qty = flt(po_qty.produced_qty)
|
||||||
|
trans_qty = flt(po_qty.material_transferred_for_manufacturing)
|
||||||
|
|
||||||
|
for item in transferred_materials:
|
||||||
|
qty= item.qty
|
||||||
|
|
||||||
|
if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
|
||||||
|
qty = (qty/trans_qty) * flt(self.fg_completed_qty)
|
||||||
|
|
||||||
|
elif backflushed_materials.get(item.item_code):
|
||||||
|
for d in backflushed_materials.get(item.item_code):
|
||||||
|
if d.get(item.warehouse):
|
||||||
|
qty-= d.get(item.warehouse)
|
||||||
|
|
||||||
|
if qty > 0:
|
||||||
|
self.add_to_stock_entry_detail({
|
||||||
|
item.item_code: {
|
||||||
|
"from_warehouse": item.warehouse,
|
||||||
|
"to_warehouse": "",
|
||||||
|
"qty": qty,
|
||||||
|
"item_name": item.item_name,
|
||||||
|
"description": item.description,
|
||||||
|
"stock_uom": item.stock_uom,
|
||||||
|
"expense_account": item.expense_account,
|
||||||
|
"cost_center": item.buying_cost_center,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
def get_pending_raw_materials(self):
|
def get_pending_raw_materials(self):
|
||||||
"""
|
"""
|
||||||
issue (item quantity) that is pending to issue or desire to transfer,
|
issue (item quantity) that is pending to issue or desire to transfer,
|
||||||
@@ -667,12 +761,56 @@ class StockEntry(StockController):
|
|||||||
if getdate(self.posting_date) > getdate(expiry_date):
|
if getdate(self.posting_date) > getdate(expiry_date):
|
||||||
frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code))
|
frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_production_order_details(production_order):
|
def get_production_order_details(production_order):
|
||||||
res = frappe.db.sql("""select bom_no, use_multi_level_bom, wip_warehouse,
|
production_order = frappe.get_doc("Production Order", production_order)
|
||||||
ifnull(qty, 0) - ifnull(produced_qty, 0) as fg_completed_qty,
|
pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty)
|
||||||
(ifnull(additional_operating_cost, 0) / qty)*(ifnull(qty, 0) - ifnull(produced_qty, 0)) as additional_operating_cost
|
|
||||||
from `tabProduction Order` where name = %s""", production_order, as_dict=1)
|
|
||||||
|
|
||||||
return res and res[0] or {}
|
return {
|
||||||
|
"from_bom": 1,
|
||||||
|
"bom_no": production_order.bom_no,
|
||||||
|
"use_multi_level_bom": production_order.use_multi_level_bom,
|
||||||
|
"wip_warehouse": production_order.wip_warehouse,
|
||||||
|
"fg_warehouse": production_order.fg_warehouse,
|
||||||
|
"fg_completed_qty": pending_qty_to_produce,
|
||||||
|
"additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce)
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
||||||
|
additional_costs = []
|
||||||
|
operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no)
|
||||||
|
if operating_cost_per_unit:
|
||||||
|
additional_costs.append({
|
||||||
|
"description": "Operating Cost as per Production Order / BOM",
|
||||||
|
"amount": operating_cost_per_unit * flt(fg_qty)
|
||||||
|
})
|
||||||
|
|
||||||
|
if production_order and production_order.additional_operating_cost:
|
||||||
|
additional_operating_cost_per_unit = \
|
||||||
|
flt(production_order.additional_operating_cost) / flt(production_order.qty)
|
||||||
|
|
||||||
|
additional_costs.append({
|
||||||
|
"description": "Additional Operating Cost",
|
||||||
|
"amount": additional_operating_cost_per_unit * flt(fg_qty)
|
||||||
|
})
|
||||||
|
|
||||||
|
return additional_costs
|
||||||
|
|
||||||
|
def get_operating_cost_per_unit(production_order=None, bom_no=None):
|
||||||
|
operating_cost_per_unit = 0
|
||||||
|
if production_order:
|
||||||
|
if not bom_no:
|
||||||
|
bom_no = production_order.bom_no
|
||||||
|
|
||||||
|
for d in production_order.get("operations"):
|
||||||
|
if flt(d.completed_qty):
|
||||||
|
operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
||||||
|
else:
|
||||||
|
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty)
|
||||||
|
|
||||||
|
# Get operating cost from BOM if not found in production_order.
|
||||||
|
if not operating_cost_per_unit and bom_no:
|
||||||
|
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
||||||
|
operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
||||||
|
|
||||||
|
return operating_cost_per_unit
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 50.0,
|
"qty": 50.0,
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 40.0,
|
"qty": 40.0,
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 45.0,
|
"qty": 45.0,
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"incoming_rate": 100,
|
"basic_rate": 100,
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 50.0,
|
"qty": 50.0,
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"incoming_rate": 5000,
|
"basic_rate": 5000,
|
||||||
"item_code": "_Test Item Home Desktop 100",
|
"item_code": "_Test Item Home Desktop 100",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 1,
|
"qty": 1,
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC",
|
create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC",
|
||||||
qty=0, rate=100)
|
qty=0, rate=100)
|
||||||
|
|
||||||
make_stock_entry(item_code=item_code, target=warehouse, qty=1, incoming_rate=10)
|
make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10)
|
||||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||||
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
||||||
|
|
||||||
# negative qty
|
# negative qty
|
||||||
make_stock_entry(item_code=item_code, source=warehouse, qty=2, incoming_rate=10)
|
make_stock_entry(item_code=item_code, source=warehouse, qty=2, basic_rate=10)
|
||||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||||
|
|
||||||
self.assertEqual([[-1, 10]], eval(sle.stock_queue))
|
self.assertEqual([[-1, 10]], eval(sle.stock_queue))
|
||||||
@@ -53,12 +53,12 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self.assertEqual([[-2, 10]], eval(sle.stock_queue))
|
self.assertEqual([[-2, 10]], eval(sle.stock_queue))
|
||||||
|
|
||||||
# move stock to positive
|
# move stock to positive
|
||||||
make_stock_entry(item_code=item_code, target=warehouse, qty=3, incoming_rate=20)
|
make_stock_entry(item_code=item_code, target=warehouse, qty=3, basic_rate=20)
|
||||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||||
self.assertEqual([[1, 20]], eval(sle.stock_queue))
|
self.assertEqual([[1, 20]], eval(sle.stock_queue))
|
||||||
|
|
||||||
# incoming entry with diff rate
|
# incoming entry with diff rate
|
||||||
make_stock_entry(item_code=item_code, target=warehouse, qty=1, incoming_rate=30)
|
make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=30)
|
||||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||||
|
|
||||||
self.assertEqual([[1, 20],[1, 30]], eval(sle.stock_queue))
|
self.assertEqual([[1, 20],[1, 30]], eval(sle.stock_queue))
|
||||||
@@ -125,7 +125,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
|
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
|
||||||
qty=50, incoming_rate=100)
|
qty=50, basic_rate=100)
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Warehouse",
|
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Warehouse",
|
||||||
"warehouse": mr.get("items")[0].t_warehouse})
|
"warehouse": mr.get("items")[0].t_warehouse})
|
||||||
@@ -152,7 +152,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
|
||||||
qty=50, incoming_rate=100)
|
qty=50, basic_rate=100)
|
||||||
|
|
||||||
mi = make_stock_entry(item_code="_Test Item", source="_Test Warehouse - _TC", qty=40)
|
mi = make_stock_entry(item_code="_Test Item", source="_Test Warehouse - _TC", qty=40)
|
||||||
|
|
||||||
@@ -217,9 +217,9 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
def test_repack_no_change_in_valuation(self):
|
def test_repack_no_change_in_valuation(self):
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||||
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
|
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
|
||||||
qty=50, incoming_rate=100)
|
qty=50, basic_rate=100)
|
||||||
|
|
||||||
repack = frappe.copy_doc(test_records[3])
|
repack = frappe.copy_doc(test_records[3])
|
||||||
repack.posting_date = nowdate()
|
repack.posting_date = nowdate()
|
||||||
@@ -238,15 +238,24 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_repack_with_change_in_valuation(self):
|
def test_repack_with_additional_costs(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||||
|
|
||||||
repack = frappe.copy_doc(test_records[3])
|
repack = frappe.copy_doc(test_records[3])
|
||||||
repack.posting_date = nowdate()
|
repack.posting_date = nowdate()
|
||||||
repack.posting_time = nowtime()
|
repack.posting_time = nowtime()
|
||||||
repack.additional_operating_cost = 1000.0
|
|
||||||
|
repack.set("additional_costs", [
|
||||||
|
{
|
||||||
|
"description": "Actual Oerating Cost",
|
||||||
|
"amount": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "additional operating costs",
|
||||||
|
"amount": 200
|
||||||
|
},
|
||||||
|
])
|
||||||
repack.insert()
|
repack.insert()
|
||||||
repack.submit()
|
repack.submit()
|
||||||
|
|
||||||
@@ -261,10 +270,12 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
stock_value_diff = flt(fg_stock_value_diff - rm_stock_value_diff, 2)
|
stock_value_diff = flt(fg_stock_value_diff - rm_stock_value_diff, 2)
|
||||||
|
|
||||||
|
self.assertEqual(stock_value_diff, 1200)
|
||||||
|
|
||||||
self.check_gl_entries("Stock Entry", repack.name,
|
self.check_gl_entries("Stock Entry", repack.name,
|
||||||
sorted([
|
sorted([
|
||||||
[stock_in_hand_account, stock_value_diff, 0.0],
|
[stock_in_hand_account, 1200, 0.0],
|
||||||
["Stock Adjustment - _TC", 0.0, stock_value_diff],
|
["Expenses Included In Valuation - _TC", 0.0, 1200.0]
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
@@ -294,7 +305,6 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
gl_entries.sort(key=lambda x: x[0])
|
gl_entries.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
self.assertEquals(expected_gl_entries[i][0], gle[0])
|
self.assertEquals(expected_gl_entries[i][0], gle[0])
|
||||||
self.assertEquals(expected_gl_entries[i][1], gle[1])
|
self.assertEquals(expected_gl_entries[i][1], gle[1])
|
||||||
@@ -503,6 +513,8 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
|
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
|
||||||
|
|
||||||
def test_production_order(self):
|
def test_production_order(self):
|
||||||
|
from erpnext.manufacturing.doctype.production_order.production_order \
|
||||||
|
import make_stock_entry as _make_stock_entry
|
||||||
bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||||
"is_default": 1, "docstatus": 1}, ["name", "operating_cost"])
|
"is_default": 1, "docstatus": 1}, ["name", "operating_cost"])
|
||||||
|
|
||||||
@@ -514,22 +526,15 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
"bom_no": bom_no,
|
"bom_no": bom_no,
|
||||||
"qty": 1.0,
|
"qty": 1.0,
|
||||||
"stock_uom": "_Test UOM",
|
"stock_uom": "_Test UOM",
|
||||||
"wip_warehouse": "_Test Warehouse - _TC"
|
"wip_warehouse": "_Test Warehouse - _TC",
|
||||||
|
"additional_operating_cost": 1000
|
||||||
})
|
})
|
||||||
production_order.insert()
|
production_order.insert()
|
||||||
production_order.submit()
|
production_order.submit()
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||||
|
|
||||||
stock_entry = frappe.new_doc("Stock Entry")
|
stock_entry = _make_stock_entry(production_order.name, "Manufacture", 1)
|
||||||
stock_entry.update({
|
|
||||||
"purpose": "Manufacture",
|
|
||||||
"production_order": production_order.name,
|
|
||||||
"bom_no": bom_no,
|
|
||||||
"fg_completed_qty": "1",
|
|
||||||
"additional_operating_cost": 1000
|
|
||||||
})
|
|
||||||
stock_entry.get_items()
|
|
||||||
|
|
||||||
rm_cost = 0
|
rm_cost = 0
|
||||||
for d in stock_entry.get("items"):
|
for d in stock_entry.get("items"):
|
||||||
@@ -538,7 +543,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
|
fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
|
||||||
self.assertEqual(fg_cost,
|
self.assertEqual(fg_cost,
|
||||||
flt(rm_cost + bom_operation_cost + stock_entry.additional_operating_cost, 2))
|
flt(rm_cost + bom_operation_cost + production_order.additional_operating_cost, 2))
|
||||||
|
|
||||||
|
|
||||||
def test_variant_production_order(self):
|
def test_variant_production_order(self):
|
||||||
@@ -610,7 +615,7 @@ def make_stock_entry(**args):
|
|||||||
"s_warehouse": args.from_warehouse or args.source,
|
"s_warehouse": args.from_warehouse or args.source,
|
||||||
"t_warehouse": args.to_warehouse or args.target,
|
"t_warehouse": args.to_warehouse or args.target,
|
||||||
"qty": args.qty,
|
"qty": args.qty,
|
||||||
"incoming_rate": args.incoming_rate,
|
"basic_rate": args.basic_rate,
|
||||||
"expense_account": args.expense_account or "Stock Adjustment - _TC",
|
"expense_account": args.expense_account or "Stock Adjustment - _TC",
|
||||||
"conversion_factor": 1.0,
|
"conversion_factor": 1.0,
|
||||||
"cost_center": "_Test Cost Center - _TC"
|
"cost_center": "_Test Cost Center - _TC"
|
||||||
|
|||||||
@@ -1,25 +1,58 @@
|
|||||||
{
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-03-29 18:22:12",
|
"creation": "2013-03-29 18:22:12",
|
||||||
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "barcode",
|
"fieldname": "barcode",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Barcode",
|
"label": "Barcode",
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"fieldname": "section_break_2",
|
"fieldname": "section_break_2",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"fieldname": "s_warehouse",
|
"fieldname": "s_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Source Warehouse",
|
"label": "Source Warehouse",
|
||||||
@@ -28,16 +61,38 @@
|
|||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 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,
|
||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "t_warehouse",
|
"fieldname": "t_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Target Warehouse",
|
"label": "Target Warehouse",
|
||||||
@@ -46,240 +101,631 @@
|
|||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 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,
|
||||||
"fieldname": "sec_break1",
|
"fieldname": "sec_break1",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "item_code",
|
"oldfieldname": "item_code",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break2",
|
"fieldname": "col_break2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "qty",
|
"fieldname": "qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Qty",
|
"label": "Qty",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "qty",
|
"oldfieldname": "qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "section_break_8",
|
"fieldname": "section_break_8",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Item Name",
|
"label": "Item Name",
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "description",
|
"oldfieldname": "description",
|
||||||
"oldfieldtype": "Text",
|
"oldfieldtype": "Text",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"print_width": "300px",
|
"print_width": "300px",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "column_break_10",
|
"fieldname": "column_break_10",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Image",
|
"label": "Image",
|
||||||
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "image_view",
|
"fieldname": "image_view",
|
||||||
"fieldtype": "Image",
|
"fieldtype": "Image",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Image View",
|
"label": "Image View",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "image",
|
"options": "image",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"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,
|
||||||
"fieldname": "quantity_and_rate",
|
"fieldname": "quantity_and_rate",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Quantity and Rate",
|
"label": "Quantity and Rate",
|
||||||
"permlevel": 0
|
"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
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "incoming_rate",
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "basic_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Valuation Rate",
|
"label": "Basic Rate (as per Stock UOM)",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "incoming_rate",
|
"oldfieldname": "incoming_rate",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "basic_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Basic Amount",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"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,
|
||||||
|
"fieldname": "additional_cost",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Additional Cost",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"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,
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "amount",
|
"oldfieldname": "amount",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "valuation_rate",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Valuation Rate",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"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,
|
||||||
"fieldname": "col_break3",
|
"fieldname": "col_break3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "uom",
|
"fieldname": "uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "uom",
|
"oldfieldname": "uom",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "conversion_factor",
|
"fieldname": "conversion_factor",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Conversion Factor",
|
"label": "Conversion Factor",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "conversion_factor",
|
"oldfieldname": "conversion_factor",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"reqd": 1
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "stock_uom",
|
"fieldname": "stock_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Stock UOM",
|
"label": "Stock UOM",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "stock_uom",
|
"oldfieldname": "stock_uom",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "transfer_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Qty as per Stock UOM",
|
||||||
|
"no_copy": 0,
|
||||||
|
"oldfieldname": "transfer_qty",
|
||||||
|
"oldfieldtype": "Currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "serial_no_batch",
|
"fieldname": "serial_no_batch",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Serial No / Batch",
|
"label": "Serial No / Batch",
|
||||||
"permlevel": 0
|
"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,
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Serial No",
|
"label": "Serial No",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "serial_no",
|
"oldfieldname": "serial_no",
|
||||||
"oldfieldtype": "Text",
|
"oldfieldtype": "Text",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break4",
|
"fieldname": "col_break4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"fieldname": "batch_no",
|
"fieldname": "batch_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Batch No",
|
"label": "Batch No",
|
||||||
|
"no_copy": 0,
|
||||||
"oldfieldname": "batch_no",
|
"oldfieldname": "batch_no",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Batch",
|
"options": "Batch",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "accounting",
|
"fieldname": "accounting",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Accounting",
|
"label": "Accounting",
|
||||||
"permlevel": 0
|
"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,
|
||||||
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
|
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
|
||||||
"fieldname": "expense_account",
|
"fieldname": "expense_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Difference Account",
|
"label": "Difference Account",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break5",
|
"fieldname": "col_break5",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"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,
|
||||||
"default": ":Company",
|
"default": ":Company",
|
||||||
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
|
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Cost Center",
|
"label": "Cost Center",
|
||||||
|
"no_copy": 0,
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "more_info",
|
"fieldname": "more_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "More Info",
|
"label": "More Info",
|
||||||
"permlevel": 0
|
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Actual Qty (at source/target)",
|
"label": "Actual Qty (at source/target)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "actual_qty",
|
"oldfieldname": "actual_qty",
|
||||||
@@ -287,67 +733,107 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 1
|
"search_index": 1,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"description": "BOM No. for a Finished Good Item",
|
"description": "BOM No. for a Finished Good Item",
|
||||||
"fieldname": "bom_no",
|
"fieldname": "bom_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "BOM No",
|
"label": "BOM No",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "BOM",
|
"options": "BOM",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "col_break6",
|
"fieldname": "col_break6",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"hidden": 0,
|
||||||
},
|
"ignore_user_permissions": 0,
|
||||||
{
|
"in_filter": 0,
|
||||||
"fieldname": "transfer_qty",
|
"in_list_view": 0,
|
||||||
"fieldtype": "Float",
|
"no_copy": 0,
|
||||||
"label": "Qty as per Stock UOM",
|
|
||||||
"oldfieldname": "transfer_qty",
|
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 0,
|
||||||
"read_only": 1,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"description": "Material Request used to make this Stock Entry",
|
"description": "Material Request used to make this Stock Entry",
|
||||||
"fieldname": "material_request",
|
"fieldname": "material_request",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Material Request",
|
"label": "Material Request",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Material Request",
|
"options": "Material Request",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
"fieldname": "material_request_item",
|
"fieldname": "material_request_item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
"label": "Material Request Item",
|
"label": "Material Request Item",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Material Request Item",
|
"options": "Material Request Item",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-07-02 05:32:56.511570",
|
"modified": "2015-08-07 13:21:23.840052",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry Detail",
|
"name": "Stock Entry Detail",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": []
|
"permissions": [],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0
|
||||||
}
|
}
|
||||||
@@ -82,13 +82,13 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
|
||||||
make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
|
make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
|
||||||
target="_Test Warehouse - _TC", qty=10, incoming_rate=700)
|
target="_Test Warehouse - _TC", qty=10, basic_rate=700)
|
||||||
|
|
||||||
make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
|
make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
|
||||||
source="_Test Warehouse - _TC", qty=15)
|
source="_Test Warehouse - _TC", qty=15)
|
||||||
|
|
||||||
make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
|
make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
|
||||||
target="_Test Warehouse - _TC", qty=15, incoming_rate=1200)
|
target="_Test Warehouse - _TC", qty=15, basic_rate=1200)
|
||||||
|
|
||||||
def create_stock_reconciliation(**args):
|
def create_stock_reconciliation(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@@ -10,6 +10,28 @@ from frappe import _
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class StockUOMReplaceUtility(Document):
|
class StockUOMReplaceUtility(Document):
|
||||||
|
|
||||||
|
# Update Stock UOM
|
||||||
|
def update_stock_uom(self):
|
||||||
|
self.validate_item()
|
||||||
|
self.validate_mandatory()
|
||||||
|
self.validate_uom_integer_type()
|
||||||
|
|
||||||
|
update_stock_ledger_entry(self.item_code, self.new_stock_uom, self.conversion_factor)
|
||||||
|
update_bin(self.item_code, self.new_stock_uom, self.conversion_factor)
|
||||||
|
update_item_master(self.item_code, self.new_stock_uom, self.conversion_factor)
|
||||||
|
|
||||||
|
#if item is template change UOM for all associated variants
|
||||||
|
if frappe.db.get_value("Item", self.item_code, "has_variants"):
|
||||||
|
for d in frappe.db.get_all("Item", filters= {"variant_of": self.item_code}):
|
||||||
|
update_stock_ledger_entry(d.name, self.new_stock_uom, self.conversion_factor)
|
||||||
|
update_bin(d.name, self.new_stock_uom, self.conversion_factor)
|
||||||
|
update_item_master(d.name, self.new_stock_uom, self.conversion_factor)
|
||||||
|
|
||||||
|
def validate_item(self):
|
||||||
|
if frappe.db.get_value("Item", self.item_code, "variant_of"):
|
||||||
|
frappe.throw(_("You cannot change default UOM of Variant. To change default UOM for Variant change default UOM of the Template"))
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
if not cstr(self.item_code):
|
if not cstr(self.item_code):
|
||||||
frappe.throw(_("Item is required"))
|
frappe.throw(_("Item is required"))
|
||||||
@@ -28,71 +50,6 @@ class StockUOMReplaceUtility(Document):
|
|||||||
if cstr(self.new_stock_uom) == cstr(stock_uom):
|
if cstr(self.new_stock_uom) == cstr(stock_uom):
|
||||||
frappe.throw(_("Item is updated"))
|
frappe.throw(_("Item is updated"))
|
||||||
|
|
||||||
def update_item_master(self):
|
|
||||||
item_doc = frappe.get_doc("Item", self.item_code)
|
|
||||||
item_doc.stock_uom = self.new_stock_uom
|
|
||||||
item_doc.save()
|
|
||||||
|
|
||||||
frappe.msgprint(_("Stock UOM updated for Item {0}").format(self.item_code))
|
|
||||||
|
|
||||||
def update_bin(self):
|
|
||||||
# update bin
|
|
||||||
if flt(self.conversion_factor) != flt(1):
|
|
||||||
frappe.db.sql("""update `tabBin`
|
|
||||||
set stock_uom = %s,
|
|
||||||
indented_qty = ifnull(indented_qty,0) * %s,
|
|
||||||
ordered_qty = ifnull(ordered_qty,0) * %s,
|
|
||||||
reserved_qty = ifnull(reserved_qty,0) * %s,
|
|
||||||
planned_qty = ifnull(planned_qty,0) * %s,
|
|
||||||
projected_qty = actual_qty + ordered_qty + indented_qty +
|
|
||||||
planned_qty - reserved_qty
|
|
||||||
where item_code = %s""", (self.new_stock_uom, self.conversion_factor,
|
|
||||||
self.conversion_factor, self.conversion_factor,
|
|
||||||
self.conversion_factor, self.item_code))
|
|
||||||
else:
|
|
||||||
frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s",
|
|
||||||
(self.new_stock_uom, self.item_code) )
|
|
||||||
|
|
||||||
# acknowledge user
|
|
||||||
frappe.msgprint(_("Stock balances updated"))
|
|
||||||
|
|
||||||
def update_stock_ledger_entry(self):
|
|
||||||
# update stock ledger entry
|
|
||||||
from erpnext.stock.stock_ledger import update_entries_after
|
|
||||||
|
|
||||||
if flt(self.conversion_factor) != flt(1):
|
|
||||||
frappe.db.sql("""update `tabStock Ledger Entry`
|
|
||||||
set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s
|
|
||||||
where item_code = %s""",
|
|
||||||
(self.new_stock_uom, self.conversion_factor, self.item_code))
|
|
||||||
else:
|
|
||||||
frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s
|
|
||||||
where item_code=%s""", (self.new_stock_uom, self.item_code))
|
|
||||||
|
|
||||||
# acknowledge user
|
|
||||||
frappe.msgprint(_("Stock Ledger entries balances updated"))
|
|
||||||
|
|
||||||
# update item valuation
|
|
||||||
if flt(self.conversion_factor) != flt(1):
|
|
||||||
wh = frappe.db.sql("select name from `tabWarehouse`")
|
|
||||||
for w in wh:
|
|
||||||
update_entries_after({"item_code": self.item_code, "warehouse": w[0]})
|
|
||||||
|
|
||||||
# acknowledge user
|
|
||||||
frappe.msgprint(_("Item valuation updated"))
|
|
||||||
|
|
||||||
# Update Stock UOM
|
|
||||||
def update_stock_uom(self):
|
|
||||||
self.validate_mandatory()
|
|
||||||
self.validate_uom_integer_type()
|
|
||||||
|
|
||||||
self.update_stock_ledger_entry()
|
|
||||||
|
|
||||||
self.update_bin()
|
|
||||||
|
|
||||||
self.update_item_master()
|
|
||||||
|
|
||||||
|
|
||||||
def validate_uom_integer_type(self):
|
def validate_uom_integer_type(self):
|
||||||
current_is_integer = frappe.db.get_value("UOM", self.current_stock_uom, "must_be_whole_number")
|
current_is_integer = frappe.db.get_value("UOM", self.current_stock_uom, "must_be_whole_number")
|
||||||
new_is_integer = frappe.db.get_value("UOM", self.new_stock_uom, "must_be_whole_number")
|
new_is_integer = frappe.db.get_value("UOM", self.new_stock_uom, "must_be_whole_number")
|
||||||
@@ -103,6 +60,53 @@ class StockUOMReplaceUtility(Document):
|
|||||||
if current_is_integer and new_is_integer and cint(self.conversion_factor)!=self.conversion_factor:
|
if current_is_integer and new_is_integer and cint(self.conversion_factor)!=self.conversion_factor:
|
||||||
frappe.throw(_("Conversion factor cannot be in fractions"))
|
frappe.throw(_("Conversion factor cannot be in fractions"))
|
||||||
|
|
||||||
|
def update_item_master(item_code, new_stock_uom, conversion_factor):
|
||||||
|
frappe.db.set_value("Item", item_code, "stock_uom", new_stock_uom)
|
||||||
|
frappe.msgprint(_("Stock UOM updated for Item {0}").format(item_code))
|
||||||
|
|
||||||
|
def update_bin(item_code, new_stock_uom, conversion_factor):
|
||||||
|
# update bin
|
||||||
|
if flt(conversion_factor) != flt(1):
|
||||||
|
frappe.db.sql("""update `tabBin`
|
||||||
|
set stock_uom = %s,
|
||||||
|
indented_qty = ifnull(indented_qty,0) * %s,
|
||||||
|
ordered_qty = ifnull(ordered_qty,0) * %s,
|
||||||
|
reserved_qty = ifnull(reserved_qty,0) * %s,
|
||||||
|
planned_qty = ifnull(planned_qty,0) * %s,
|
||||||
|
projected_qty = actual_qty + ordered_qty + indented_qty +
|
||||||
|
planned_qty - reserved_qty
|
||||||
|
where item_code = %s""", (new_stock_uom, conversion_factor,
|
||||||
|
conversion_factor, conversion_factor,
|
||||||
|
conversion_factor, item_code))
|
||||||
|
else:
|
||||||
|
frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s",
|
||||||
|
(new_stock_uom, item_code) )
|
||||||
|
|
||||||
|
def update_stock_ledger_entry(item_code, new_stock_uom, conversion_factor):
|
||||||
|
# update stock ledger entry
|
||||||
|
from erpnext.stock.stock_ledger import update_entries_after
|
||||||
|
|
||||||
|
if flt(conversion_factor) != flt(1):
|
||||||
|
frappe.db.sql("""update `tabStock Ledger Entry`
|
||||||
|
set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s
|
||||||
|
where item_code = %s""",
|
||||||
|
(new_stock_uom, conversion_factor, item_code))
|
||||||
|
else:
|
||||||
|
frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s
|
||||||
|
where item_code=%s""", (new_stock_uom, item_code))
|
||||||
|
|
||||||
|
# acknowledge user
|
||||||
|
frappe.msgprint(_("Stock Ledger entries balances updated"))
|
||||||
|
|
||||||
|
# update item valuation
|
||||||
|
if flt(conversion_factor) != flt(1):
|
||||||
|
wh = frappe.db.sql("select name from `tabWarehouse`")
|
||||||
|
for w in wh:
|
||||||
|
update_entries_after({"item_code": item_code, "warehouse": w[0]})
|
||||||
|
|
||||||
|
# acknowledge user
|
||||||
|
frappe.msgprint(_("Item valuation updated"))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_stock_uom(item_code):
|
def get_stock_uom(item_code):
|
||||||
return { 'current_stock_uom': cstr(frappe.db.get_value('Item', item_code, 'stock_uom')) }
|
return { 'current_stock_uom': cstr(frappe.db.get_value('Item', item_code, 'stock_uom')) }
|
||||||
|
|||||||
@@ -401,7 +401,6 @@ def apply_price_list_on_item(args):
|
|||||||
item_details = frappe._dict()
|
item_details = frappe._dict()
|
||||||
item_doc = frappe.get_doc("Item", args.item_code)
|
item_doc = frappe.get_doc("Item", args.item_code)
|
||||||
get_price_list_rate(args, item_doc, item_details)
|
get_price_list_rate(args, item_doc, item_details)
|
||||||
item_details.discount_percentage = 0.0
|
|
||||||
item_details.update(get_pricing_rule_for_item(args))
|
item_details.update(get_pricing_rule_for_item(args))
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% var visible_columns = row.get_visible_columns(["item_code",
|
{% var visible_columns = row.get_visible_columns(["item_code",
|
||||||
"item_name", "amount", "stock_uom", "uom", "qty",
|
"item_name", "amount", "stock_uom", "uom", "qty",
|
||||||
"s_warehouse", "t_warehouse", "incoming_rate"]);
|
"s_warehouse", "t_warehouse", "valuation_rate"]);
|
||||||
%}
|
%}
|
||||||
|
|
||||||
{% if(!doc) { %}
|
{% if(!doc) { %}
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<div class="col-sm-2 col-xs-2 text-right">
|
<div class="col-sm-2 col-xs-2 text-right">
|
||||||
{%= doc.get_formatted("amount") %}
|
{%= doc.get_formatted("amount") %}
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
{%= doc.get_formatted("incoming_rate") %}
|
{%= doc.get_formatted("valuation_rate") %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def get_product_list(search=None, start=0, limit=10):
|
|||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
d.route = ((d.parent_website_route + "/") if d.parent_website_route else "") \
|
d.route = ((d.parent_website_route + "/") if d.parent_website_route else "") \
|
||||||
+ d.page_name
|
+ (d.page_name or "")
|
||||||
|
|
||||||
return [get_item_for_list_in_html(r) for r in data]
|
return [get_item_for_list_in_html(r) for r in data]
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Pincode",
|
"label": "Postal Code",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Phone",
|
"label": "Phone",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fax",
|
"fieldname": "fax",
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
"icon": "icon-map-marker",
|
"icon": "icon-map-marker",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_dialog": 0,
|
"in_dialog": 0,
|
||||||
"modified": "2015-06-01 06:42:18.331818",
|
"modified": "2015-08-10 19:42:18.331819",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Utilities",
|
"module": "Utilities",
|
||||||
"name": "Address",
|
"name": "Address",
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ def repost_all_stock_vouchers():
|
|||||||
|
|
||||||
doc = frappe.get_doc(voucher_type, voucher_no)
|
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||||
if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
|
if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
|
||||||
doc.get_stock_and_rate(force=1)
|
doc.calculate_rate_and_amount(force=1)
|
||||||
elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
|
elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
|
||||||
doc.validate()
|
doc.validate()
|
||||||
|
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
version = "5.5.1"
|
version = "5.6.0"
|
||||||
|
|
||||||
with open("requirements.txt", "r") as f:
|
with open("requirements.txt", "r") as f:
|
||||||
install_requires = f.readlines()
|
install_requires = f.readlines()
|
||||||
|
|||||||
16
sponsors.md
16
sponsors.md
@@ -29,5 +29,21 @@
|
|||||||
For Sales / Purchase Return Enhancement <a href="https://github.com/frappe/erpnext/issues/3582">#3582</a>
|
For Sales / Purchase Return Enhancement <a href="https://github.com/frappe/erpnext/issues/3582">#3582</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 30%">
|
||||||
|
PT. Ridho Sribumi Sejahtera
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
For Additional Costs in Stock Entry <a href="https://github.com/frappe/erpnext/issues/3613">#3613</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 30%">
|
||||||
|
<a href="http://www.rigpl.com">Rohit Industries</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
For Mandrill Integration <a href="https://github.com/frappe/erpnext/issues/3546">#3546</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user