Merge branch 'saurabh6790-pi_update_stock' into develop
This commit is contained in:
commit
972224e71a
@ -4,6 +4,7 @@
|
|||||||
frappe.provide("erpnext.accounts");
|
frappe.provide("erpnext.accounts");
|
||||||
{% include 'buying/doctype/purchase_common/purchase_common.js' %};
|
{% include 'buying/doctype/purchase_common/purchase_common.js' %};
|
||||||
|
|
||||||
|
|
||||||
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||||
onload: function() {
|
onload: function() {
|
||||||
this._super();
|
this._super();
|
||||||
@ -19,49 +20,57 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
refresh: function(doc) {
|
refresh: function(doc) {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
|
hide_fields(this.frm.doc);
|
||||||
|
|
||||||
// Show / Hide button
|
// Show / Hide button
|
||||||
this.show_general_ledger();
|
this.show_general_ledger();
|
||||||
|
|
||||||
if(!doc.is_return) {
|
if(doc.update_stock==1 && doc.docstatus==1) {
|
||||||
if(doc.docstatus==1) {
|
this.show_stock_ledger();
|
||||||
if(doc.outstanding_amount > 0) {
|
}
|
||||||
this.frm.add_custom_button(__('Payment'), this.make_bank_entry, __("Make"));
|
|
||||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
if(!doc.is_return && doc.docstatus==1) {
|
||||||
}
|
if(doc.outstanding_amount > 0) {
|
||||||
if(doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) {
|
this.frm.add_custom_button(__('Payment'), this.make_bank_entry, __("Make"));
|
||||||
cur_frm.add_custom_button(__('Debit Note'), this.make_debit_note, __("Make"));
|
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus===0) {
|
if(doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) {
|
||||||
cur_frm.add_custom_button(__('Purchase Order'), function() {
|
cur_frm.add_custom_button(doc.update_stock ? __('Purchase Return') : __('Debit Note'),
|
||||||
frappe.model.map_current_doc({
|
this.make_debit_note, __("Make"));
|
||||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
|
|
||||||
source_doctype: "Purchase Order",
|
|
||||||
get_query_filters: {
|
|
||||||
supplier: cur_frm.doc.supplier || undefined,
|
|
||||||
docstatus: 1,
|
|
||||||
status: ["!=", "Closed"],
|
|
||||||
per_billed: ["<", 99.99],
|
|
||||||
company: cur_frm.doc.company
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, __("Get items from"));
|
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('Purchase Receipt'), function() {
|
|
||||||
frappe.model.map_current_doc({
|
|
||||||
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
|
|
||||||
source_doctype: "Purchase Receipt",
|
|
||||||
get_query_filters: {
|
|
||||||
supplier: cur_frm.doc.supplier || undefined,
|
|
||||||
docstatus: 1,
|
|
||||||
status: ["!=", "Closed"],
|
|
||||||
company: cur_frm.doc.company
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, __("Get items from"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(doc.docstatus===0) {
|
||||||
|
cur_frm.add_custom_button(__('Purchase Order'), function() {
|
||||||
|
frappe.model.map_current_doc({
|
||||||
|
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
|
||||||
|
source_doctype: "Purchase Order",
|
||||||
|
get_query_filters: {
|
||||||
|
supplier: cur_frm.doc.supplier || undefined,
|
||||||
|
docstatus: 1,
|
||||||
|
status: ["!=", "Closed"],
|
||||||
|
per_billed: ["<", 99.99],
|
||||||
|
company: cur_frm.doc.company
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, __("Get items from"));
|
||||||
|
|
||||||
|
cur_frm.add_custom_button(__('Purchase Receipt'), function() {
|
||||||
|
frappe.model.map_current_doc({
|
||||||
|
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
|
||||||
|
source_doctype: "Purchase Receipt",
|
||||||
|
get_query_filters: {
|
||||||
|
supplier: cur_frm.doc.supplier || undefined,
|
||||||
|
docstatus: 1,
|
||||||
|
status: ["!=", "Closed"],
|
||||||
|
company: cur_frm.doc.company
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, __("Get items from"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
|
||||||
},
|
},
|
||||||
|
|
||||||
supplier: function() {
|
supplier: function() {
|
||||||
@ -100,19 +109,33 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
is_paid: function() {
|
||||||
|
hide_fields(this.frm.doc);
|
||||||
|
if(cint(this.frm.doc.is_paid)) {
|
||||||
|
if(!this.frm.doc.company) {
|
||||||
|
cur_frm.set_value("is_paid", 0)
|
||||||
|
msgprint(__("Please specify Company to proceed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.calculate_outstanding_amount();
|
||||||
|
this.frm.refresh_fields();
|
||||||
|
},
|
||||||
|
|
||||||
write_off_amount: function() {
|
write_off_amount: function() {
|
||||||
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
|
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
|
||||||
this.calculate_outstanding_amount();
|
this.calculate_outstanding_amount();
|
||||||
this.frm.refresh_fields();
|
this.frm.refresh_fields();
|
||||||
},
|
},
|
||||||
|
|
||||||
allocated_amount: function() {
|
paid_amount: function() {
|
||||||
this.calculate_total_advance();
|
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
|
||||||
|
this.write_off_outstanding_amount();
|
||||||
this.frm.refresh_fields();
|
this.frm.refresh_fields();
|
||||||
},
|
},
|
||||||
|
|
||||||
tc_name: function() {
|
allocated_amount: function() {
|
||||||
this.get_terms();
|
this.calculate_total_advance();
|
||||||
|
this.frm.refresh_fields();
|
||||||
},
|
},
|
||||||
|
|
||||||
items_add: function(doc, cdt, cdn) {
|
items_add: function(doc, cdt, cdn) {
|
||||||
@ -137,6 +160,44 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
|
|
||||||
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
||||||
|
|
||||||
|
// Hide Fields
|
||||||
|
// ------------
|
||||||
|
function hide_fields(doc) {
|
||||||
|
parent_fields = ['due_date', 'is_opening', 'advances_section', 'from_date', 'to_date'];
|
||||||
|
|
||||||
|
if(cint(doc.is_paid) == 1) {
|
||||||
|
hide_field(parent_fields);
|
||||||
|
} else {
|
||||||
|
for (i in parent_fields) {
|
||||||
|
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
|
||||||
|
if(!docfield.hidden) unhide_field(parent_fields[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty'];
|
||||||
|
|
||||||
|
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
|
||||||
|
(cint(doc.update_stock)==1 ? true : false));
|
||||||
|
|
||||||
|
cur_frm.refresh_fields();
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_frm.cscript.update_stock = function(doc, dt, dn) {
|
||||||
|
hide_fields(doc, dt, dn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
|
||||||
|
return {
|
||||||
|
filters: [
|
||||||
|
["Account", "account_type", "in", ["Cash", "Bank"]],
|
||||||
|
["Account", "root_type", "=", "Asset"],
|
||||||
|
["Account", "is_group", "=",0],
|
||||||
|
["Account", "company", "=", doc.company]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cur_frm.cscript.make_bank_entry = function() {
|
cur_frm.cscript.make_bank_entry = function() {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
|
||||||
@ -267,3 +328,29 @@ cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
|
|||||||
else
|
else
|
||||||
cur_frm.pformat.print_heading = __("Purchase Invoice");
|
cur_frm.pformat.print_heading = __("Purchase Invoice");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frappe.ui.form.on("Purchase Invoice", {
|
||||||
|
onload: function(frm) {
|
||||||
|
$.each(["warehouse", "rejected_warehouse"], function(i, field) {
|
||||||
|
frm.set_query(field, "items", function() {
|
||||||
|
return {
|
||||||
|
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("supplier_warehouse", function() {
|
||||||
|
return {
|
||||||
|
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
is_subcontracted: function(frm) {
|
||||||
|
if (frm.doc.is_subcontracted === "Yes") {
|
||||||
|
erpnext.buying.get_default_bom(frm);
|
||||||
|
}
|
||||||
|
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
@ -116,6 +116,31 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "is_paid",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Is Paid",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -262,8 +287,6 @@
|
|||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "company",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -279,6 +302,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"default": "0",
|
||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -803,6 +827,32 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "update_stock",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Update Stock",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -1663,6 +1713,162 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 1,
|
||||||
|
"collapsible_depends_on": "paid_amount",
|
||||||
|
"depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)",
|
||||||
|
"fieldname": "payments_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Payments",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "mode_of_payment",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Mode of Payment",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Mode of Payment",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "cash_bank_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Cash/Bank Account",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "col_br_payments",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "is_paid",
|
||||||
|
"fieldname": "paid_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Paid Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "base_paid_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Paid Amount (Company Currency)",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -1975,15 +2181,15 @@
|
|||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 1,
|
"collapsible": 0,
|
||||||
"fieldname": "printing_settings",
|
"fieldname": "raw_materials_supplied",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Printing Settings",
|
"label": "Raw Materials Supplied",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -2001,21 +2207,102 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "language",
|
"default": "No",
|
||||||
"fieldtype": "Data",
|
"fieldname": "is_subcontracted",
|
||||||
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Print Language",
|
"label": "Raw Materials Supplied",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "No\nYes",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "supplier_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Supplier Warehouse",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Warehouse",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"print_width": "50px",
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "supplied_items",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Supplied Items",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Purchase Receipt Item Supplied",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "printing_settings",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Printing Settings",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -2263,28 +2550,28 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "posting_time",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Time",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Mode of Payment",
|
"label": "Posting Time",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 1,
|
||||||
"oldfieldname": "mode_of_payment",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "Mode of Payment",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0,
|
||||||
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2312,6 +2599,33 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"description": "Warehouse where you are maintaining stock of rejected items",
|
||||||
|
"fieldname": "rejected_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Rejected Warehouse",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Warehouse",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -2369,35 +2683,8 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "is_recurring",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "Select the period when the invoice will be generated automatically",
|
||||||
"fieldname": "recurring_id",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Reference Document",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Purchase Invoice",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
|
||||||
"description": "",
|
|
||||||
"fieldname": "recurring_type",
|
"fieldname": "recurring_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -2405,7 +2692,7 @@
|
|||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Frequency",
|
"label": "Recurring Type",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||||
@ -2423,16 +2710,16 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "Start date of current invoice's period",
|
||||||
"fieldname": "repeat_on_day_of_month",
|
"fieldname": "from_date",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Repeat on Day of Month",
|
"label": "From Date",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -2449,16 +2736,16 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "End date of current invoice's period",
|
||||||
"fieldname": "end_date",
|
"fieldname": "to_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Recurring Ends On",
|
"label": "To Date",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -2528,19 +2815,18 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc",
|
||||||
"fieldname": "notification_email_address",
|
"fieldname": "repeat_on_day_of_month",
|
||||||
"fieldtype": "Code",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Notification Email Address",
|
"label": "Repeat on Day of Month",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Email",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
@ -2552,24 +2838,23 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"fieldname": "recurring_print_format",
|
"description": "The date on which recurring invoice will be stop",
|
||||||
"fieldtype": "Link",
|
"fieldname": "end_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Recurring Print Format",
|
"label": "End Date",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 1,
|
||||||
"options": "Print Format",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"print_hide": 1,
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -2603,25 +2888,51 @@
|
|||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "is_recurring",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||||
"fieldname": "from_date",
|
"fieldname": "next_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "From Date",
|
"label": "Next Date",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
|
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||||
|
"fieldname": "recurring_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Recurring Id",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -2632,16 +2943,16 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "is_recurring",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"description": "Enter email id separated by commas, invoice will be mailed automatically on particular date",
|
||||||
"fieldname": "to_date",
|
"fieldname": "notification_email_address",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "To Date",
|
"label": "Notification Email Address",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -2658,20 +2969,21 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "is_recurring",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "",
|
"fieldname": "recurring_print_format",
|
||||||
"fieldname": "next_date",
|
"fieldtype": "Link",
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Next Date",
|
"label": "Recurring Print Format",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
|
"options": "Print Format",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -2692,7 +3004,7 @@
|
|||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": 0,
|
"menu_index": 0,
|
||||||
"modified": "2016-04-06 05:39:45.475873",
|
"modified": "2016-04-11 14:37:27.243253",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cint, formatdate, flt, getdate
|
from frappe.utils import cint, formatdate, flt, getdate
|
||||||
from frappe import msgprint, _, throw
|
from frappe import _, throw
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
@ -12,6 +12,9 @@ from erpnext.controllers.buying_controller import BuyingController
|
|||||||
from erpnext.accounts.party import get_party_account, get_due_date
|
from erpnext.accounts.party import get_party_account, get_due_date
|
||||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
|
||||||
|
from erpnext.controllers.stock_controller import get_warehouse_account
|
||||||
|
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
|
||||||
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -45,19 +48,31 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.validate_supplier_invoice()
|
self.validate_supplier_invoice()
|
||||||
self.validate_advance_jv("Purchase Order")
|
self.validate_advance_jv("Purchase Order")
|
||||||
|
|
||||||
|
# validate cash purchase
|
||||||
|
if (self.is_paid == 1):
|
||||||
|
self.validate_cash()
|
||||||
|
|
||||||
self.check_conversion_rate()
|
self.check_conversion_rate()
|
||||||
self.validate_credit_to_acc()
|
self.validate_credit_to_acc()
|
||||||
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
|
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
|
||||||
self.check_for_closed_status()
|
self.check_for_closed_status()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
|
self.set_expense_account()
|
||||||
self.set_against_expense_account()
|
self.set_against_expense_account()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
self.update_valuation_rate("items")
|
|
||||||
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
||||||
self.validate_fixed_asset_account()
|
self.validate_fixed_asset_account()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
|
|
||||||
|
def validate_cash(self):
|
||||||
|
if not self.cash_bank_account and flt(self.paid_amount):
|
||||||
|
frappe.throw(_("Cash or Bank Account is mandatory for making payment entry"))
|
||||||
|
|
||||||
|
if flt(self.paid_amount) + flt(self.write_off_amount) \
|
||||||
|
- flt(self.base_grand_total) > 1/(10**(self.precision("base_grand_total") + 1)):
|
||||||
|
frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
|
||||||
|
|
||||||
def create_remarks(self):
|
def create_remarks(self):
|
||||||
if not self.remarks:
|
if not self.remarks:
|
||||||
if self.bill_no and self.bill_date:
|
if self.bill_no and self.bill_date:
|
||||||
@ -136,51 +151,54 @@ class PurchaseInvoice(BuyingController):
|
|||||||
["Purchase Receipt", "purchase_receipt", "pr_detail"]
|
["Purchase Receipt", "purchase_receipt", "pr_detail"]
|
||||||
])
|
])
|
||||||
|
|
||||||
def set_against_expense_account(self):
|
def set_expense_account(self):
|
||||||
auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
||||||
|
|
||||||
if auto_accounting_for_stock:
|
if auto_accounting_for_stock:
|
||||||
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
||||||
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
|
if self.update_stock:
|
||||||
|
warehouse_account = get_warehouse_account()
|
||||||
|
|
||||||
against_accounts = []
|
|
||||||
stock_items = self.get_stock_items()
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
# in case of auto inventory accounting,
|
# in case of auto inventory accounting,
|
||||||
# expense account is always "Stock Received But Not Billed" for a stock item
|
# expense account is always "Stock Received But Not Billed" for a stock item
|
||||||
# except epening entry, drop-ship entry and fixed asset items
|
# except epening entry, drop-ship entry and fixed asset items
|
||||||
|
|
||||||
if auto_accounting_for_stock and item.item_code in stock_items and self.is_opening == 'No' \
|
if auto_accounting_for_stock and self.is_opening == 'No' \
|
||||||
and (not item.po_detail
|
and item.item_code in stock_items and ((not item.po_detail
|
||||||
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")
|
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier"))
|
||||||
or not frappe.db.get_value("Item", item.item_code, "is_fixed_asset")):
|
or not frappe.db.get_value("Item", item.item_code, "is_fixed_asset")):
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
if self.update_stock:
|
||||||
item.cost_center = None
|
item.expense_account = warehouse_account[item.warehouse]["name"]
|
||||||
|
else:
|
||||||
if stock_not_billed_account not in against_accounts:
|
item.expense_account = stock_not_billed_account
|
||||||
against_accounts.append(stock_not_billed_account)
|
|
||||||
|
|
||||||
elif not item.expense_account:
|
elif not item.expense_account:
|
||||||
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
||||||
|
|
||||||
elif item.expense_account not in against_accounts:
|
def set_against_expense_account(self):
|
||||||
# if no auto_accounting_for_stock or not a stock item
|
against_accounts = []
|
||||||
|
for item in self.get("items"):
|
||||||
|
if item.expense_account not in against_accounts:
|
||||||
against_accounts.append(item.expense_account)
|
against_accounts.append(item.expense_account)
|
||||||
|
|
||||||
self.against_expense_account = ",".join(against_accounts)
|
self.against_expense_account = ",".join(against_accounts)
|
||||||
|
|
||||||
def po_required(self):
|
def po_required(self):
|
||||||
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.purchase_order:
|
if not d.purchase_order:
|
||||||
throw(_("Purchse Order number required for Item {0}").format(d.item_code))
|
throw(_("Purchse Order number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def pr_required(self):
|
def pr_required(self):
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
|
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.purchase_receipt and d.item_code in stock_items:
|
if not d.purchase_receipt and d.item_code in stock_items:
|
||||||
throw(_("Purchase Receipt number required for Item {0}").format(d.item_code))
|
throw(_("Purchase Receipt number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def validate_write_off_account(self):
|
def validate_write_off_account(self):
|
||||||
if self.write_off_amount and not self.write_off_account:
|
if self.write_off_amount and not self.write_off_account:
|
||||||
@ -228,21 +246,71 @@ class PurchaseInvoice(BuyingController):
|
|||||||
from erpnext.accounts.utils import reconcile_against_document
|
from erpnext.accounts.utils import reconcile_against_document
|
||||||
reconcile_against_document(lst)
|
reconcile_against_document(lst)
|
||||||
|
|
||||||
|
def update_status_updater_args(self):
|
||||||
|
if cint(self.update_stock):
|
||||||
|
self.status_updater.extend([{
|
||||||
|
'source_dt': 'Purchase Invoice Item',
|
||||||
|
'target_dt': 'Purchase Order Item',
|
||||||
|
'join_field': 'po_detail',
|
||||||
|
'target_field': 'received_qty',
|
||||||
|
'target_parent_dt': 'Purchase Order',
|
||||||
|
'target_parent_field': 'per_received',
|
||||||
|
'target_ref_field': 'qty',
|
||||||
|
'source_field': 'qty',
|
||||||
|
'percent_join_field':'purchase_order',
|
||||||
|
# 'percent_join_field': 'prevdoc_docname',
|
||||||
|
'overflow_type': 'receipt',
|
||||||
|
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||||
|
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'source_dt': 'Purchase Invoice Item',
|
||||||
|
'target_dt': 'Purchase Order Item',
|
||||||
|
'join_field': 'po_detail',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Purchase Order',
|
||||||
|
# 'target_parent_field': 'per_received',
|
||||||
|
# 'target_ref_field': 'qty',
|
||||||
|
'source_field': '-1 * qty',
|
||||||
|
# 'percent_join_field': 'prevdoc_docname',
|
||||||
|
# 'overflow_type': 'receipt',
|
||||||
|
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||||
|
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
def validate_purchase_receipt_if_update_stock(self):
|
||||||
|
if self.update_stock:
|
||||||
|
for item in self.get("items"):
|
||||||
|
if item.purchase_receipt:
|
||||||
|
frappe.throw(_("Stock cannot be updated against Purchase Receipt {0}")
|
||||||
|
.format(item.purchase_receipt))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.check_prev_docstatus()
|
self.check_prev_docstatus()
|
||||||
|
self.update_status_updater_args()
|
||||||
|
|
||||||
self.validate_asset()
|
self.validate_asset()
|
||||||
|
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
self.company, self.base_grand_total)
|
self.company, self.base_grand_total)
|
||||||
|
|
||||||
# this sequence because outstanding may get -negative
|
|
||||||
self.make_gl_entries()
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
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")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
|
if self.update_stock == 1:
|
||||||
|
self.update_stock_ledger()
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||||
|
update_serial_nos_after_submit(self, "items")
|
||||||
|
|
||||||
|
# this sequence because outstanding may get -negative
|
||||||
|
self.make_gl_entries()
|
||||||
|
|
||||||
self.update_project()
|
self.update_project()
|
||||||
|
|
||||||
def validate_asset(self):
|
def validate_asset(self):
|
||||||
@ -272,21 +340,50 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if self.docstatus==1 and not asset.supplier:
|
if self.docstatus==1 and not asset.supplier:
|
||||||
frappe.db.set_value("Asset", asset.name, "supplier", self.supplier)
|
frappe.db.set_value("Asset", asset.name, "supplier", self.supplier)
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self, repost_future_gle=False):
|
||||||
auto_accounting_for_stock = \
|
self.auto_accounting_for_stock = \
|
||||||
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
||||||
|
|
||||||
stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
|
self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||||
|
self.negative_expense_to_be_booked = 0.0
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
# parent's gl entry
|
|
||||||
|
self.make_supplier_gl_entry(gl_entries)
|
||||||
|
self.make_item_gl_entries(gl_entries)
|
||||||
|
self.make_tax_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
gl_entries = merge_similar_entries(gl_entries)
|
||||||
|
|
||||||
|
self.make_payment_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
self.make_write_off_gl_entry(gl_entries)
|
||||||
|
|
||||||
|
if gl_entries:
|
||||||
|
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
|
||||||
|
|
||||||
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
|
||||||
|
update_outstanding=update_outstanding, merge_entries=False)
|
||||||
|
|
||||||
|
if update_outstanding == "No":
|
||||||
|
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
|
||||||
|
self.doctype, self.return_against if cint(self.is_return) else self.name)
|
||||||
|
|
||||||
|
if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock:
|
||||||
|
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||||
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
|
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
|
||||||
|
|
||||||
|
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
|
||||||
|
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||||
|
|
||||||
|
|
||||||
|
def make_supplier_gl_entry(self, gl_entries):
|
||||||
if self.grand_total:
|
if self.grand_total:
|
||||||
# Didnot use base_grand_total to book rounding loss gle
|
# Didnot use base_grand_total to book rounding loss gle
|
||||||
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
|
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
|
||||||
self.precision("grand_total"))
|
self.precision("grand_total"))
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": self.credit_to,
|
"account": self.credit_to,
|
||||||
@ -301,6 +398,88 @@ class PurchaseInvoice(BuyingController):
|
|||||||
}, self.party_account_currency)
|
}, self.party_account_currency)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def make_item_gl_entries(self, gl_entries):
|
||||||
|
# item gl entries
|
||||||
|
stock_items = self.get_stock_items()
|
||||||
|
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||||
|
warehouse_account = get_warehouse_account()
|
||||||
|
|
||||||
|
for item in self.get("items"):
|
||||||
|
if flt(item.base_net_amount):
|
||||||
|
account_currency = get_account_currency(item.expense_account)
|
||||||
|
|
||||||
|
if self.update_stock and self.auto_accounting_for_stock:
|
||||||
|
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
|
||||||
|
|
||||||
|
# warehouse account
|
||||||
|
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
|
||||||
|
* flt(item.qty) * flt(item.conversion_factor), item.precision("base_net_amount"))
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": item.expense_account,
|
||||||
|
"against": self.supplier,
|
||||||
|
"debit": warehouse_debit_amount,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"cost_center": item.cost_center
|
||||||
|
}, account_currency)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Amount added through landed-cost-voucher
|
||||||
|
if flt(item.landed_cost_voucher_amount):
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": expenses_included_in_valuation,
|
||||||
|
"against": item.expense_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": flt(item.landed_cost_voucher_amount)
|
||||||
|
}))
|
||||||
|
|
||||||
|
# sub-contracting warehouse
|
||||||
|
if flt(item.rm_supp_cost):
|
||||||
|
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["name"]
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": supplier_warehouse_account,
|
||||||
|
"against": item.expense_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": flt(item.rm_supp_cost)
|
||||||
|
}, warehouse_account[self.supplier_warehouse]["account_currency"]))
|
||||||
|
else:
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": item.expense_account,
|
||||||
|
"against": self.supplier,
|
||||||
|
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
|
"debit_in_account_currency": (flt(item.base_net_amount,
|
||||||
|
item.precision("base_net_amount")) if account_currency==self.company_currency
|
||||||
|
else flt(item.net_amount, item.precision("net_amount"))),
|
||||||
|
"cost_center": item.cost_center
|
||||||
|
}, account_currency)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.auto_accounting_for_stock and self.is_opening == "No" and \
|
||||||
|
item.item_code in stock_items and item.item_tax_amount:
|
||||||
|
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
||||||
|
if item.purchase_receipt:
|
||||||
|
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
||||||
|
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
||||||
|
(item.purchase_receipt, self.expenses_included_in_valuation))
|
||||||
|
|
||||||
|
if not negative_expense_booked_in_pr:
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": self.stock_received_but_not_billed,
|
||||||
|
"against": self.supplier,
|
||||||
|
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
|
||||||
|
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.negative_expense_to_be_booked += flt(item.item_tax_amount, \
|
||||||
|
item.precision("item_tax_amount"))
|
||||||
|
|
||||||
|
def make_tax_gl_entries(self, gl_entries):
|
||||||
# tax table gl entries
|
# tax table gl entries
|
||||||
valuation_tax = {}
|
valuation_tax = {}
|
||||||
for tax in self.get("taxes"):
|
for tax in self.get("taxes"):
|
||||||
@ -320,69 +499,31 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"cost_center": tax.cost_center
|
"cost_center": tax.cost_center
|
||||||
}, account_currency)
|
}, account_currency)
|
||||||
)
|
)
|
||||||
|
|
||||||
# accumulate valuation tax
|
# accumulate valuation tax
|
||||||
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
||||||
if auto_accounting_for_stock and not tax.cost_center:
|
if self.auto_accounting_for_stock and not tax.cost_center:
|
||||||
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
||||||
valuation_tax.setdefault(tax.cost_center, 0)
|
valuation_tax.setdefault(tax.cost_center, 0)
|
||||||
valuation_tax[tax.cost_center] += \
|
valuation_tax[tax.cost_center] += \
|
||||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
||||||
|
|
||||||
# item gl entries
|
if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
|
||||||
negative_expense_to_be_booked = 0.0
|
|
||||||
stock_items = self.get_stock_items()
|
|
||||||
for item in self.get("items"):
|
|
||||||
if flt(item.base_net_amount):
|
|
||||||
account_currency = get_account_currency(item.expense_account)
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict({
|
|
||||||
"account": item.expense_account,
|
|
||||||
"against": self.supplier,
|
|
||||||
"debit": item.base_net_amount,
|
|
||||||
"debit_in_account_currency": item.base_net_amount \
|
|
||||||
if account_currency==self.company_currency else item.net_amount,
|
|
||||||
"cost_center": item.cost_center
|
|
||||||
}, account_currency)
|
|
||||||
)
|
|
||||||
|
|
||||||
if auto_accounting_for_stock and self.is_opening == "No" and \
|
|
||||||
item.item_code in stock_items and item.item_tax_amount:
|
|
||||||
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
|
||||||
if item.purchase_receipt:
|
|
||||||
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
|
||||||
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
|
||||||
(item.purchase_receipt, expenses_included_in_valuation))
|
|
||||||
|
|
||||||
if not negative_expense_booked_in_pr:
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict({
|
|
||||||
"account": stock_received_but_not_billed,
|
|
||||||
"against": self.supplier,
|
|
||||||
"debit": flt(item.item_tax_amount, self.precision("item_tax_amount", item)),
|
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
negative_expense_to_be_booked += flt(item.item_tax_amount, self.precision("item_tax_amount", item))
|
|
||||||
|
|
||||||
if self.is_opening == "No" and negative_expense_to_be_booked and valuation_tax:
|
|
||||||
# credit valuation tax amount in "Expenses Included In Valuation"
|
# credit valuation tax amount in "Expenses Included In Valuation"
|
||||||
# this will balance out valuation amount included in cost of goods sold
|
# this will balance out valuation amount included in cost of goods sold
|
||||||
|
|
||||||
total_valuation_amount = sum(valuation_tax.values())
|
total_valuation_amount = sum(valuation_tax.values())
|
||||||
amount_including_divisional_loss = negative_expense_to_be_booked
|
amount_including_divisional_loss = self.negative_expense_to_be_booked
|
||||||
i = 1
|
i = 1
|
||||||
for cost_center, amount in valuation_tax.items():
|
for cost_center, amount in valuation_tax.items():
|
||||||
if i == len(valuation_tax):
|
if i == len(valuation_tax):
|
||||||
applicable_amount = amount_including_divisional_loss
|
applicable_amount = amount_including_divisional_loss
|
||||||
else:
|
else:
|
||||||
applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount)
|
applicable_amount = self.negative_expense_to_be_booked * (amount / total_valuation_amount)
|
||||||
amount_including_divisional_loss -= applicable_amount
|
amount_including_divisional_loss -= applicable_amount
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": expenses_included_in_valuation,
|
"account": self.expenses_included_in_valuation,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"credit": applicable_amount,
|
"credit": applicable_amount,
|
||||||
@ -392,6 +533,36 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
def make_payment_gl_entries(self, gl_entries):
|
||||||
|
# Make Cash GL Entries
|
||||||
|
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
|
||||||
|
bank_account_currency = get_account_currency(self.cash_bank_account)
|
||||||
|
# CASH, make payment entries
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": self.credit_to,
|
||||||
|
"party_type": "Supplier",
|
||||||
|
"party": self.supplier,
|
||||||
|
"against": self.cash_bank_account,
|
||||||
|
"debit": self.base_paid_amount,
|
||||||
|
"debit_in_account_currency": self.base_paid_amount \
|
||||||
|
if self.party_account_currency==self.company_currency else self.paid_amount,
|
||||||
|
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||||
|
"against_voucher_type": self.doctype,
|
||||||
|
}, self.party_account_currency)
|
||||||
|
)
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": self.cash_bank_account,
|
||||||
|
"against": self.supplier,
|
||||||
|
"credit": self.base_paid_amount,
|
||||||
|
"credit_in_account_currency": self.base_paid_amount \
|
||||||
|
if bank_account_currency==self.company_currency else self.paid_amount
|
||||||
|
}, bank_account_currency)
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_write_off_gl_entry(self, gl_entries):
|
||||||
# writeoff account includes petty difference in the invoice amount
|
# writeoff account includes petty difference in the invoice amount
|
||||||
# and the amount that is paid
|
# and the amount that is paid
|
||||||
if self.write_off_account and flt(self.write_off_amount):
|
if self.write_off_account and flt(self.write_off_amount):
|
||||||
@ -421,13 +592,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if gl_entries:
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
|
||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.check_for_closed_status()
|
self.check_for_closed_status()
|
||||||
|
|
||||||
|
self.update_status_updater_args()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||||
remove_against_link_from_jv(self.doctype, self.name)
|
remove_against_link_from_jv(self.doctype, self.name)
|
||||||
@ -436,6 +605,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
|
if self.update_stock == 1:
|
||||||
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.make_gl_entries_on_cancel()
|
self.make_gl_entries_on_cancel()
|
||||||
self.update_project()
|
self.update_project()
|
||||||
self.validate_asset()
|
self.validate_asset()
|
||||||
|
|||||||
@ -6,11 +6,12 @@ from __future__ import unicode_literals
|
|||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
import frappe.model
|
import frappe.model
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint, flt
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||||
test_records as pr_test_records
|
test_records as pr_test_records
|
||||||
from erpnext.exceptions import InvalidCurrency
|
from erpnext.exceptions import InvalidCurrency
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
||||||
|
|
||||||
test_dependencies = ["Item", "Cost Center"]
|
test_dependencies = ["Item", "Cost Center"]
|
||||||
test_ignore = ["Serial No"]
|
test_ignore = ["Serial No"]
|
||||||
@ -119,20 +120,20 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_purchase_invoice_calculation(self):
|
def test_purchase_invoice_calculation(self):
|
||||||
wrapper = frappe.copy_doc(test_records[0])
|
pi = frappe.copy_doc(test_records[0])
|
||||||
wrapper.insert()
|
pi.insert()
|
||||||
wrapper.load_from_db()
|
pi.load_from_db()
|
||||||
|
|
||||||
expected_values = [
|
expected_values = [
|
||||||
["_Test Item Home Desktop 100", 90, 59],
|
["_Test Item Home Desktop 100", 90, 59],
|
||||||
["_Test Item Home Desktop 200", 135, 177]
|
["_Test Item Home Desktop 200", 135, 177]
|
||||||
]
|
]
|
||||||
for i, item in enumerate(wrapper.get("items")):
|
for i, item in enumerate(pi.get("items")):
|
||||||
self.assertEqual(item.item_code, expected_values[i][0])
|
self.assertEqual(item.item_code, expected_values[i][0])
|
||||||
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
||||||
self.assertEqual(item.valuation_rate, expected_values[i][2])
|
self.assertEqual(item.valuation_rate, expected_values[i][2])
|
||||||
|
|
||||||
self.assertEqual(wrapper.base_net_total, 1250)
|
self.assertEqual(pi.base_net_total, 1250)
|
||||||
|
|
||||||
# tax amounts
|
# tax amounts
|
||||||
expected_values = [
|
expected_values = [
|
||||||
@ -146,7 +147,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
["_Test Account Discount - _TC", 168.03, 1512.30],
|
["_Test Account Discount - _TC", 168.03, 1512.30],
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, tax in enumerate(wrapper.get("taxes")):
|
for i, tax in enumerate(pi.get("taxes")):
|
||||||
self.assertEqual(tax.account_head, expected_values[i][0])
|
self.assertEqual(tax.account_head, expected_values[i][0])
|
||||||
self.assertEqual(tax.tax_amount, expected_values[i][1])
|
self.assertEqual(tax.tax_amount, expected_values[i][1])
|
||||||
self.assertEqual(tax.total, expected_values[i][2])
|
self.assertEqual(tax.total, expected_values[i][2])
|
||||||
@ -315,6 +316,88 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
|
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||||
|
set_perpetual_inventory()
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
|
posting_time=frappe.utils.nowtime())
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||||
|
debit_in_account_currency, credit_in_account_currency
|
||||||
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
|
order by account asc""", pi.name, as_dict=1)
|
||||||
|
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
expected_gl_entries = dict((d[0], d) for d in [
|
||||||
|
[pi.credit_to, 0.0, 250.0],
|
||||||
|
[pi.items[0].warehouse, 250.0, 0.0]
|
||||||
|
])
|
||||||
|
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][0], gle.account)
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][1], gle.debit)
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][2], gle.credit)
|
||||||
|
|
||||||
|
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||||
|
set_perpetual_inventory()
|
||||||
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
|
posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - _TC", is_paid=1)
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
|
||||||
|
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
|
||||||
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
|
group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
|
||||||
|
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
expected_gl_entries = dict((d[0], d) for d in [
|
||||||
|
[pi.credit_to, 250.0, 250.0],
|
||||||
|
[pi.items[0].warehouse, 250.0, 0.0],
|
||||||
|
["Cash - _TC", 0.0, 250.0]
|
||||||
|
])
|
||||||
|
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][0], gle.account)
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][1], gle.debit)
|
||||||
|
self.assertEquals(expected_gl_entries[gle.account][2], gle.credit)
|
||||||
|
|
||||||
|
def test_update_stock_and_purchase_return(self):
|
||||||
|
actual_qty_0 = get_qty_after_transaction()
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
|
posting_time=frappe.utils.nowtime())
|
||||||
|
|
||||||
|
actual_qty_1 = get_qty_after_transaction()
|
||||||
|
self.assertEquals(actual_qty_0 + 5, actual_qty_1)
|
||||||
|
|
||||||
|
# return entry
|
||||||
|
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
||||||
|
|
||||||
|
actual_qty_2 = get_qty_after_transaction()
|
||||||
|
self.assertEquals(actual_qty_1 - 2, actual_qty_2)
|
||||||
|
|
||||||
|
pi1.cancel()
|
||||||
|
self.assertEquals(actual_qty_1, get_qty_after_transaction())
|
||||||
|
|
||||||
|
pi.cancel()
|
||||||
|
self.assertEquals(actual_qty_0, get_qty_after_transaction())
|
||||||
|
|
||||||
|
def test_subcontracting_via_purchase_invoice(self):
|
||||||
|
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, basic_rate=100)
|
||||||
|
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
|
||||||
|
qty=100, basic_rate=100)
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(item_code="_Test FG Item", qty=10, rate=500,
|
||||||
|
update_stock=1, is_subcontracted="Yes")
|
||||||
|
|
||||||
|
self.assertEquals(len(pi.get("supplied_items")), 2)
|
||||||
|
|
||||||
|
rm_supp_cost = sum([d.amount for d in pi.get("supplied_items")])
|
||||||
|
self.assertEquals(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
|
||||||
|
|
||||||
def make_purchase_invoice(**args):
|
def make_purchase_invoice(**args):
|
||||||
pi = frappe.new_doc("Purchase Invoice")
|
pi = frappe.new_doc("Purchase Invoice")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@ -322,12 +405,22 @@ def make_purchase_invoice(**args):
|
|||||||
pi.posting_date = args.posting_date
|
pi.posting_date = args.posting_date
|
||||||
if args.posting_time:
|
if args.posting_time:
|
||||||
pi.posting_time = args.posting_time
|
pi.posting_time = args.posting_time
|
||||||
|
if args.update_stock:
|
||||||
|
pi.update_stock = 1
|
||||||
|
if args.is_paid:
|
||||||
|
pi.is_paid = 1
|
||||||
|
|
||||||
|
if args.cash_bank_account:
|
||||||
|
pi.cash_bank_account=args.cash_bank_account
|
||||||
|
|
||||||
pi.company = args.company or "_Test Company"
|
pi.company = args.company or "_Test Company"
|
||||||
pi.supplier = args.supplier or "_Test Supplier"
|
pi.supplier = args.supplier or "_Test Supplier"
|
||||||
pi.currency = args.currency or "INR"
|
pi.currency = args.currency or "INR"
|
||||||
pi.conversion_rate = args.conversion_rate or 1
|
pi.conversion_rate = args.conversion_rate or 1
|
||||||
pi.is_return = args.is_return
|
pi.is_return = args.is_return
|
||||||
pi.return_against = args.return_against
|
pi.return_against = args.return_against
|
||||||
|
pi.is_subcontracted = args.is_subcontracted
|
||||||
|
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
||||||
|
|
||||||
pi.append("items", {
|
pi.append("items", {
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
|
|||||||
@ -22,7 +22,8 @@
|
|||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 10,
|
"qty": 10,
|
||||||
"rate": 50,
|
"rate": 50,
|
||||||
"uom": "_Test UOM"
|
"uom": "_Test UOM",
|
||||||
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amount": 750,
|
"amount": 750,
|
||||||
@ -37,7 +38,8 @@
|
|||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 5,
|
"qty": 5,
|
||||||
"rate": 150,
|
"rate": 150,
|
||||||
"uom": "_Test UOM"
|
"uom": "_Test UOM",
|
||||||
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grand_total": 0,
|
"grand_total": 0,
|
||||||
|
|||||||
@ -213,6 +213,31 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "received_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Received Qty",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "2",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
@ -239,6 +264,31 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "rejected_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Rejected Qty",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "2",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -763,6 +813,185 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "warehouse_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Warehouse",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"default": "",
|
||||||
|
"fieldname": "warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Accepted Warehouse",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Warehouse",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "rejected_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Rejected Warehouse",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Warehouse",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "batch_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Batch No",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Batch",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "col_br_wh",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "serial_no",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Serial No",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "rejected_serial_no",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Rejected Serial No",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -1053,6 +1282,32 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "bom",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "BOM",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "BOM",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -1206,7 +1461,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "valuation_rate",
|
"fieldname": "valuation_rate",
|
||||||
@ -1254,6 +1509,31 @@
|
|||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "landed_cost_voucher_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Landed Cost Voucher Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
@ -1265,7 +1545,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-03-28 05:05:27.752823",
|
"modified": "2016-04-07 16:38:30.502386",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
@ -1274,5 +1554,6 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
}
|
}
|
||||||
@ -278,20 +278,20 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
|
|||||||
// Hide Fields
|
// Hide Fields
|
||||||
// ------------
|
// ------------
|
||||||
cur_frm.cscript.hide_fields = function(doc) {
|
cur_frm.cscript.hide_fields = function(doc) {
|
||||||
par_flds = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances_received',
|
parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances_received',
|
||||||
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
|
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
|
||||||
|
|
||||||
if(cint(doc.is_pos) == 1) {
|
if(cint(doc.is_pos) == 1) {
|
||||||
hide_field(par_flds);
|
hide_field(parent_fields);
|
||||||
} else {
|
} else {
|
||||||
for (i in par_flds) {
|
for (i in parent_fields) {
|
||||||
var docfield = frappe.meta.docfield_map[doc.doctype][par_flds[i]];
|
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
|
||||||
if(!docfield.hidden) unhide_field(par_flds[i]);
|
if(!docfield.hidden) unhide_field(parent_fields[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item_flds_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse', 'expense_account', 'warehouse']
|
item_fields_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse', 'expense_account', 'warehouse']
|
||||||
cur_frm.fields_dict['items'].grid.set_column_disp(item_flds_stock,
|
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
|
||||||
(cint(doc.update_stock)==1 ? true : false));
|
(cint(doc.update_stock)==1 ? true : false));
|
||||||
|
|
||||||
// India related fields
|
// India related fields
|
||||||
@ -303,25 +303,6 @@ cur_frm.cscript.hide_fields = function(doc) {
|
|||||||
cur_frm.refresh_fields();
|
cur_frm.refresh_fields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cur_frm.cscript.mode_of_payment = function(doc) {
|
|
||||||
if(doc.is_pos) {
|
|
||||||
return cur_frm.call({
|
|
||||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
|
||||||
args: {
|
|
||||||
"mode_of_payment": doc.mode_of_payment,
|
|
||||||
"company": doc.company
|
|
||||||
},
|
|
||||||
callback: function(r, rt) {
|
|
||||||
if(r.message) {
|
|
||||||
cur_frm.set_value("cash_bank_account", r.message["account"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.update_stock = function(doc, dt, dn) {
|
cur_frm.cscript.update_stock = function(doc, dt, dn) {
|
||||||
cur_frm.cscript.hide_fields(doc, dt, dn);
|
cur_frm.cscript.hide_fields(doc, dt, dn);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,13 +82,9 @@ class SalesInvoice(SellingController):
|
|||||||
self.update_packing_list()
|
self.update_packing_list()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if cint(self.update_stock) == 1:
|
if not self.recurring_id:
|
||||||
self.update_stock_ledger()
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
else:
|
self.company, self.base_grand_total, self)
|
||||||
# Check for Approving Authority
|
|
||||||
if not self.recurring_id:
|
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
|
||||||
self.company, self.base_grand_total, self)
|
|
||||||
|
|
||||||
self.check_prev_docstatus()
|
self.check_prev_docstatus()
|
||||||
|
|
||||||
@ -100,6 +96,11 @@ class SalesInvoice(SellingController):
|
|||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
self.update_billing_status_in_dn()
|
self.update_billing_status_in_dn()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
|
if self.update_stock == 1:
|
||||||
|
self.update_stock_ledger()
|
||||||
|
|
||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
@ -117,9 +118,6 @@ class SalesInvoice(SellingController):
|
|||||||
self.update_time_log_batch(None)
|
self.update_time_log_batch(None)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
if cint(self.update_stock) == 1:
|
|
||||||
self.update_stock_ledger()
|
|
||||||
|
|
||||||
self.check_close_sales_order("sales_order")
|
self.check_close_sales_order("sales_order")
|
||||||
|
|
||||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||||
@ -138,6 +136,11 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
self.validate_c_form_on_cancel()
|
self.validate_c_form_on_cancel()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
|
if self.update_stock == 1:
|
||||||
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.make_gl_entries_on_cancel()
|
self.make_gl_entries_on_cancel()
|
||||||
|
|
||||||
def update_status_updater_args(self):
|
def update_status_updater_args(self):
|
||||||
@ -461,21 +464,6 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
d.income_account = disposal_account
|
d.income_account = disposal_account
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
if cint(self.is_pos) == 1:
|
|
||||||
if flt(self.paid_amount) == 0:
|
|
||||||
if self.cash_bank_account:
|
|
||||||
frappe.db.set(self, 'paid_amount',
|
|
||||||
flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
|
|
||||||
else:
|
|
||||||
# show message that the amount is not paid
|
|
||||||
frappe.db.set(self,'paid_amount',0)
|
|
||||||
frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
|
||||||
else:
|
|
||||||
frappe.db.set(self,'paid_amount',0)
|
|
||||||
|
|
||||||
frappe.db.set(self, 'base_paid_amount',
|
|
||||||
flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
|
|
||||||
|
|
||||||
def check_prev_docstatus(self):
|
def check_prev_docstatus(self):
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
|
|||||||
@ -25,7 +25,6 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd
|
|||||||
def process_gl_map(gl_map, merge_entries=True):
|
def process_gl_map(gl_map, merge_entries=True):
|
||||||
if merge_entries:
|
if merge_entries:
|
||||||
gl_map = merge_similar_entries(gl_map)
|
gl_map = merge_similar_entries(gl_map)
|
||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
# toggle debit, credit if negative entry
|
# toggle debit, credit if negative entry
|
||||||
if flt(entry.debit) < 0:
|
if flt(entry.debit) < 0:
|
||||||
|
|||||||
@ -42,7 +42,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
this.frm.set_query("item_code", "items", function() {
|
this.frm.set_query("item_code", "items", function() {
|
||||||
if(me.frm.doc.is_subcontracted == "Yes") {
|
if(me.frm.doc.is_subcontracted == "Yes") {
|
||||||
return{
|
return{
|
||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
filters:{ 'is_sub_contracted_item': 1 }
|
filters:{ 'is_sub_contracted_item': 1 }
|
||||||
}
|
}
|
||||||
@ -114,8 +114,49 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
qty: function(doc, cdt, cdn) {
|
qty: function(doc, cdt, cdn) {
|
||||||
|
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && doc.update_stock)) {
|
||||||
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
||||||
|
if(!(item.received_qty || item.rejected_qty) && item.qty) {
|
||||||
|
item.received_qty = item.qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item.qty > item.received_qty) {
|
||||||
|
msgprint(__("Error: {0} > {1}", [__(frappe.meta.get_label(item.doctype, "qty", item.name)),
|
||||||
|
__(frappe.meta.get_label(item.doctype, "received_qty", item.name))]))
|
||||||
|
item.qty = item.rejected_qty = 0.0;
|
||||||
|
} else {
|
||||||
|
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._super(doc, cdt, cdn);
|
this._super(doc, cdt, cdn);
|
||||||
this.conversion_factor(doc, cdt, cdn);
|
this.conversion_factor(doc, cdt, cdn);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
received_qty: function(doc, cdt, cdn) {
|
||||||
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
||||||
|
|
||||||
|
item.qty = (item.qty < item.received_qty) ? item.qty : item.received_qty;
|
||||||
|
this.qty(doc, cdt, cdn);
|
||||||
|
},
|
||||||
|
|
||||||
|
rejected_qty: function(doc, cdt, cdn) {
|
||||||
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
|
||||||
|
|
||||||
|
if(item.rejected_qty > item.received_qty) {
|
||||||
|
msgprint(__("Error: {0} > {1}", [__(frappe.meta.get_label(item.doctype, "rejected_qty", item.name)),
|
||||||
|
__(frappe.meta.get_label(item.doctype, "received_qty", item.name))]));
|
||||||
|
item.qty = item.rejected_qty = 0.0;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.qty(doc, cdt, cdn);
|
||||||
},
|
},
|
||||||
|
|
||||||
conversion_factor: function(doc, cdt, cdn) {
|
conversion_factor: function(doc, cdt, cdn) {
|
||||||
@ -194,6 +235,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
erpnext.utils.get_address_display(this.frm, "shipping_address",
|
erpnext.utils.get_address_display(this.frm, "shipping_address",
|
||||||
"shipping_address_display", is_your_company_address=true)
|
"shipping_address_display", is_your_company_address=true)
|
||||||
|
},
|
||||||
|
|
||||||
|
tc_name: function() {
|
||||||
|
this.get_terms();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,32 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
po.load_from_db()
|
po.load_from_db()
|
||||||
self.assertEquals(po.get("items")[0].received_qty, 4)
|
self.assertEquals(po.get("items")[0].received_qty, 4)
|
||||||
|
|
||||||
|
def test_ordered_qty_against_pi_with_update_stock(self):
|
||||||
|
existing_ordered_qty = get_ordered_qty()
|
||||||
|
|
||||||
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10)
|
||||||
|
|
||||||
|
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(po.name)
|
||||||
|
pi.update_stock = 1
|
||||||
|
pi.items[0].qty = 12
|
||||||
|
pi.insert()
|
||||||
|
pi.submit()
|
||||||
|
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEquals(po.get("items")[0].received_qty, 12)
|
||||||
|
|
||||||
|
pi.cancel()
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEquals(po.get("items")[0].received_qty, 0)
|
||||||
|
|
||||||
def test_make_purchase_invoice(self):
|
def test_make_purchase_invoice(self):
|
||||||
po = create_purchase_order(do_not_submit=True)
|
po = create_purchase_order(do_not_submit=True)
|
||||||
|
|
||||||
|
|||||||
@ -55,6 +55,26 @@ class AccountsController(TransactionBase):
|
|||||||
validate_recurring_document(self)
|
validate_recurring_document(self)
|
||||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||||
|
|
||||||
|
self.validate_paid_amount()
|
||||||
|
|
||||||
|
def validate_paid_amount(self):
|
||||||
|
if hasattr(self, "is_pos") or hasattr(self, "is_paid"):
|
||||||
|
is_paid = self.get("is_pos") or self.get("is_paid")
|
||||||
|
if cint(is_paid) == 1:
|
||||||
|
if flt(self.paid_amount) == 0:
|
||||||
|
if self.cash_bank_account:
|
||||||
|
self.paid_amount = flt(flt(self.grand_total) - flt(self.write_off_amount),
|
||||||
|
self.precision("paid_amount"))
|
||||||
|
else:
|
||||||
|
# show message that the amount is not paid
|
||||||
|
self.paid_amount = 0
|
||||||
|
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
||||||
|
else:
|
||||||
|
frappe.db.set(self,'paid_amount',0)
|
||||||
|
|
||||||
|
frappe.db.set(self, 'base_paid_amount',
|
||||||
|
flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
if self.meta.get_field("is_recurring"):
|
if self.meta.get_field("is_recurring"):
|
||||||
validate_recurring_document(self)
|
validate_recurring_document(self)
|
||||||
@ -126,14 +146,6 @@ class AccountsController(TransactionBase):
|
|||||||
"""set missing item values"""
|
"""set missing item values"""
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
|
|
||||||
if self.doctype == "Purchase Invoice":
|
|
||||||
auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
|
||||||
|
|
||||||
if auto_accounting_for_stock:
|
|
||||||
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
|
||||||
|
|
||||||
stock_items = self.get_stock_items()
|
|
||||||
|
|
||||||
if hasattr(self, "items"):
|
if hasattr(self, "items"):
|
||||||
parent_dict = {}
|
parent_dict = {}
|
||||||
for fieldname in self.meta.get_valid_columns():
|
for fieldname in self.meta.get_valid_columns():
|
||||||
@ -180,14 +192,8 @@ class AccountsController(TransactionBase):
|
|||||||
item.rate = flt(item.price_list_rate *
|
item.rate = flt(item.price_list_rate *
|
||||||
(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
|
(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
|
||||||
|
|
||||||
if self.doctype == "Purchase Invoice":
|
if self.doctype == "Purchase Invoice":
|
||||||
if auto_accounting_for_stock and item.item_code in stock_items \
|
self.set_expense_account()
|
||||||
and self.is_opening == 'No' \
|
|
||||||
and (not item.po_detail or not frappe.db.get_value("Purchase Order Item",
|
|
||||||
item.po_detail, "delivered_by_supplier")):
|
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
|
||||||
item.cost_center = None
|
|
||||||
|
|
||||||
def set_taxes(self):
|
def set_taxes(self):
|
||||||
if not self.meta.get_field("taxes"):
|
if not self.meta.get_field("taxes"):
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, msgprint
|
from frappe import _, msgprint
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt,cint, cstr
|
||||||
|
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
from erpnext.accounts.party import get_party_details
|
from erpnext.accounts.party import get_party_details
|
||||||
@ -33,6 +33,25 @@ class BuyingController(StockController):
|
|||||||
self.validate_stock_or_nonstock_items()
|
self.validate_stock_or_nonstock_items()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
|
|
||||||
|
if self.doctype=="Purchase Invoice":
|
||||||
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
|
|
||||||
|
if self.doctype=="Purchase Receipt" or (self.doctype=="Purchase Invoice" and self.update_stock):
|
||||||
|
self.validate_purchase_return()
|
||||||
|
self.validate_rejected_warehouse()
|
||||||
|
self.validate_accepted_rejected_qty()
|
||||||
|
|
||||||
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
|
pc_obj.validate_for_items(self)
|
||||||
|
|
||||||
|
#sub-contracting
|
||||||
|
self.validate_for_subcontracting()
|
||||||
|
self.create_raw_materials_supplied("supplied_items")
|
||||||
|
self.set_landed_cost_voucher_amount()
|
||||||
|
|
||||||
|
if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
|
||||||
|
self.update_valuation_rate("items")
|
||||||
|
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate=False):
|
||||||
super(BuyingController, self).set_missing_values(for_validate)
|
super(BuyingController, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
@ -60,6 +79,13 @@ class BuyingController(StockController):
|
|||||||
if tax_for_valuation:
|
if tax_for_valuation:
|
||||||
frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
|
frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
|
||||||
|
|
||||||
|
def set_landed_cost_voucher_amount(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
lc_voucher_amount = frappe.db.sql("""select sum(applicable_charges)
|
||||||
|
from `tabLanded Cost Item`
|
||||||
|
where docstatus = 1 and purchase_receipt_item = %s""", d.name)
|
||||||
|
d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0
|
||||||
|
|
||||||
def set_total_in_words(self):
|
def set_total_in_words(self):
|
||||||
from frappe.utils import money_in_words
|
from frappe.utils import money_in_words
|
||||||
company_currency = get_company_currency(self.company)
|
company_currency = get_company_currency(self.company)
|
||||||
@ -94,7 +120,6 @@ class BuyingController(StockController):
|
|||||||
if item.item_code and item.qty and item.item_code in stock_items:
|
if item.item_code and item.qty and item.item_code in stock_items:
|
||||||
item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \
|
item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \
|
||||||
else flt(item.qty) / stock_items_qty
|
else flt(item.qty) / stock_items_qty
|
||||||
|
|
||||||
if i == (last_stock_item_idx - 1):
|
if i == (last_stock_item_idx - 1):
|
||||||
item.item_tax_amount = flt(valuation_amount_adjustment,
|
item.item_tax_amount = flt(valuation_amount_adjustment,
|
||||||
self.precision("item_tax_amount", item))
|
self.precision("item_tax_amount", item))
|
||||||
@ -108,10 +133,10 @@ class BuyingController(StockController):
|
|||||||
item.conversion_factor = get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0
|
item.conversion_factor = get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0
|
||||||
|
|
||||||
qty_in_stock_uom = flt(item.qty * item.conversion_factor)
|
qty_in_stock_uom = flt(item.qty * item.conversion_factor)
|
||||||
rm_supp_cost = flt(item.rm_supp_cost) if self.doctype=="Purchase Receipt" else 0.0
|
rm_supp_cost = flt(item.rm_supp_cost) if self.doctype in ["Purchase Receipt", "Purchase Invoice"] else 0.0
|
||||||
|
|
||||||
landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \
|
landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \
|
||||||
if self.doctype == "Purchase Receipt" else 0.0
|
if self.doctype in ["Purchase Receipt", "Purchase Invoice"] else 0.0
|
||||||
|
|
||||||
item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + rm_supp_cost
|
item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + rm_supp_cost
|
||||||
+ landed_cost_voucher_amount) / qty_in_stock_uom)
|
+ landed_cost_voucher_amount) / qty_in_stock_uom)
|
||||||
@ -123,7 +148,7 @@ class BuyingController(StockController):
|
|||||||
frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
|
frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
|
||||||
|
|
||||||
if self.is_subcontracted == "Yes":
|
if self.is_subcontracted == "Yes":
|
||||||
if self.doctype == "Purchase Receipt" and not self.supplier_warehouse:
|
if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
|
||||||
frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))
|
frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))
|
||||||
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
@ -139,7 +164,7 @@ class BuyingController(StockController):
|
|||||||
if self.is_subcontracted=="Yes":
|
if self.is_subcontracted=="Yes":
|
||||||
parent_items = []
|
parent_items = []
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if self.doctype == "Purchase Receipt":
|
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||||
item.rm_supp_cost = 0.0
|
item.rm_supp_cost = 0.0
|
||||||
if item.item_code in self.sub_contracted_items:
|
if item.item_code in self.sub_contracted_items:
|
||||||
self.update_raw_materials_supplied(item, raw_material_table)
|
self.update_raw_materials_supplied(item, raw_material_table)
|
||||||
@ -149,7 +174,7 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
self.cleanup_raw_materials_supplied(parent_items, raw_material_table)
|
self.cleanup_raw_materials_supplied(parent_items, raw_material_table)
|
||||||
|
|
||||||
elif self.doctype == "Purchase Receipt":
|
elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
item.rm_supp_cost = 0.0
|
item.rm_supp_cost = 0.0
|
||||||
|
|
||||||
@ -179,7 +204,7 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
rm.conversion_factor = item.conversion_factor
|
rm.conversion_factor = item.conversion_factor
|
||||||
|
|
||||||
if self.doctype == "Purchase Receipt":
|
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||||
rm.consumed_qty = required_qty
|
rm.consumed_qty = required_qty
|
||||||
rm.description = bom_item.description
|
rm.description = bom_item.description
|
||||||
if item.batch_no and not rm.batch_no:
|
if item.batch_no and not rm.batch_no:
|
||||||
@ -205,7 +230,7 @@ class BuyingController(StockController):
|
|||||||
rm.amount = required_qty * flt(rm.rate)
|
rm.amount = required_qty * flt(rm.rate)
|
||||||
raw_materials_cost += flt(rm.amount)
|
raw_materials_cost += flt(rm.amount)
|
||||||
|
|
||||||
if self.doctype == "Purchase Receipt":
|
if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
|
||||||
item.rm_supp_cost = raw_materials_cost
|
item.rm_supp_cost = raw_materials_cost
|
||||||
|
|
||||||
def cleanup_raw_materials_supplied(self, parent_items, raw_material_table):
|
def cleanup_raw_materials_supplied(self, parent_items, raw_material_table):
|
||||||
@ -261,3 +286,106 @@ class BuyingController(StockController):
|
|||||||
if not d.conversion_factor:
|
if not d.conversion_factor:
|
||||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
|
def validate_purchase_return(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
if self.is_return and flt(d.rejected_qty) != 0:
|
||||||
|
frappe.throw(_("Row #{0}: Rejected Qty can not be entered in Purchase Return").format(d.idx))
|
||||||
|
|
||||||
|
# validate rate with ref PR
|
||||||
|
|
||||||
|
def validate_rejected_warehouse(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
||||||
|
if self.rejected_warehouse:
|
||||||
|
d.rejected_warehouse = self.rejected_warehouse
|
||||||
|
|
||||||
|
if not d.rejected_warehouse:
|
||||||
|
frappe.throw(_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(d.idx, d.item_code))
|
||||||
|
|
||||||
|
# validate accepted and rejected qty
|
||||||
|
def validate_accepted_rejected_qty(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
if not flt(d.received_qty) and flt(d.qty):
|
||||||
|
d.received_qty = flt(d.qty) - flt(d.rejected_qty)
|
||||||
|
|
||||||
|
elif not flt(d.qty) and flt(d.rejected_qty):
|
||||||
|
d.qty = flt(d.received_qty) - flt(d.rejected_qty)
|
||||||
|
|
||||||
|
elif not flt(d.rejected_qty):
|
||||||
|
d.rejected_qty = flt(d.received_qty) - flt(d.qty)
|
||||||
|
|
||||||
|
# Check Received Qty = Accepted Qty + Rejected Qty
|
||||||
|
if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)):
|
||||||
|
frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
|
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||||
|
self.update_ordered_qty()
|
||||||
|
|
||||||
|
sl_entries = []
|
||||||
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
|
for d in self.get('items'):
|
||||||
|
if d.item_code in stock_items and d.warehouse:
|
||||||
|
pr_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
|
if pr_qty:
|
||||||
|
val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9
|
||||||
|
rate = flt(d.valuation_rate, val_rate_db_precision)
|
||||||
|
sle = self.get_sl_entries(d, {
|
||||||
|
"actual_qty": flt(pr_qty),
|
||||||
|
"serial_no": cstr(d.serial_no).strip()
|
||||||
|
})
|
||||||
|
if self.is_return:
|
||||||
|
sle.update({
|
||||||
|
"outgoing_rate": rate
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
sle.update({
|
||||||
|
"incoming_rate": rate
|
||||||
|
})
|
||||||
|
sl_entries.append(sle)
|
||||||
|
|
||||||
|
if flt(d.rejected_qty) > 0:
|
||||||
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
|
"warehouse": d.rejected_warehouse,
|
||||||
|
"actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
|
||||||
|
"serial_no": cstr(d.rejected_serial_no).strip(),
|
||||||
|
"incoming_rate": 0.0
|
||||||
|
}))
|
||||||
|
|
||||||
|
self.make_sl_entries_for_supplier_warehouse(sl_entries)
|
||||||
|
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
|
||||||
|
via_landed_cost_voucher=via_landed_cost_voucher)
|
||||||
|
|
||||||
|
def update_ordered_qty(self):
|
||||||
|
po_map = {}
|
||||||
|
for d in self.get("items"):
|
||||||
|
if self.doctype=="Purchase Receipt" \
|
||||||
|
and d.prevdoc_doctype=="Purchase Order" and d.prevdoc_detail_docname:
|
||||||
|
po_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
||||||
|
|
||||||
|
elif self.doctype=="Purchase Invoice" and d.purchase_order and d.po_detail:
|
||||||
|
po_map.setdefault(d.purchase_order, []).append(d.po_detail)
|
||||||
|
|
||||||
|
for po, po_item_rows in po_map.items():
|
||||||
|
if po and po_item_rows:
|
||||||
|
po_obj = frappe.get_doc("Purchase Order", po)
|
||||||
|
|
||||||
|
if po_obj.status in ["Closed", "Cancelled"]:
|
||||||
|
frappe.throw(_("{0} {1} is cancelled or closed").format(_("Purchase Order"), po),
|
||||||
|
frappe.InvalidStatusError)
|
||||||
|
|
||||||
|
po_obj.update_ordered_qty(po_item_rows)
|
||||||
|
|
||||||
|
def make_sl_entries_for_supplier_warehouse(self, sl_entries):
|
||||||
|
if hasattr(self, 'supplied_items'):
|
||||||
|
for d in self.get('supplied_items'):
|
||||||
|
# negative quantity is passed, as raw material qty has to be decreased
|
||||||
|
# when PR is submitted and it has to be increased when PR is cancelled
|
||||||
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
|
"item_code": d.rm_item_code,
|
||||||
|
"warehouse": self.supplier_warehouse,
|
||||||
|
"actual_qty": -1*flt(d.consumed_qty),
|
||||||
|
}))
|
||||||
|
|
||||||
|
|||||||
@ -152,6 +152,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
target_doc.prevdoc_docname = source_doc.prevdoc_docname
|
target_doc.prevdoc_docname = source_doc.prevdoc_docname
|
||||||
target_doc.prevdoc_detail_docname = source_doc.prevdoc_detail_docname
|
target_doc.prevdoc_detail_docname = source_doc.prevdoc_detail_docname
|
||||||
elif doctype == "Purchase Invoice":
|
elif doctype == "Purchase Invoice":
|
||||||
|
target_doc.received_qty = -1* source_doc.qty
|
||||||
target_doc.purchase_order = source_doc.purchase_order
|
target_doc.purchase_order = source_doc.purchase_order
|
||||||
target_doc.purchase_receipt = source_doc.purchase_receipt
|
target_doc.purchase_receipt = source_doc.purchase_receipt
|
||||||
target_doc.po_detail = source_doc.po_detail
|
target_doc.po_detail = source_doc.po_detail
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from frappe.utils import cint, flt, cstr, comma_or
|
|||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
from frappe import _, throw
|
from frappe import _, throw
|
||||||
from erpnext.stock.get_item_details import get_bin_details
|
from erpnext.stock.get_item_details import get_bin_details
|
||||||
|
from erpnext.stock.utils import get_incoming_rate
|
||||||
|
|
||||||
from erpnext.controllers.stock_controller import StockController
|
from erpnext.controllers.stock_controller import StockController
|
||||||
|
|
||||||
@ -229,6 +230,79 @@ class SellingController(StockController):
|
|||||||
if status == "Closed":
|
if status == "Closed":
|
||||||
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
|
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
|
||||||
|
|
||||||
|
def update_reserved_qty(self):
|
||||||
|
so_map = {}
|
||||||
|
for d in self.get("items"):
|
||||||
|
if d.so_detail:
|
||||||
|
if self.doctype == "Delivery Note" and d.against_sales_order:
|
||||||
|
so_map.setdefault(d.against_sales_order, []).append(d.so_detail)
|
||||||
|
elif self.doctype == "Sales Invoice" and d.sales_order and self.update_stock:
|
||||||
|
so_map.setdefault(d.sales_order, []).append(d.so_detail)
|
||||||
|
|
||||||
|
for so, so_item_rows in so_map.items():
|
||||||
|
if so and so_item_rows:
|
||||||
|
sales_order = frappe.get_doc("Sales Order", so)
|
||||||
|
|
||||||
|
if sales_order.status in ["Closed", "Cancelled"]:
|
||||||
|
frappe.throw(_("{0} {1} is cancelled or closed").format(_("Sales Order"), so),
|
||||||
|
frappe.InvalidStatusError)
|
||||||
|
|
||||||
|
sales_order.update_reserved_qty(so_item_rows)
|
||||||
|
|
||||||
|
def update_stock_ledger(self):
|
||||||
|
self.update_reserved_qty()
|
||||||
|
|
||||||
|
sl_entries = []
|
||||||
|
for d in self.get_item_list():
|
||||||
|
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
|
||||||
|
return_rate = 0
|
||||||
|
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||||
|
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
||||||
|
|
||||||
|
# On cancellation or if return entry submission, make stock ledger entry for
|
||||||
|
# target warehouse first, to update serial no values properly
|
||||||
|
|
||||||
|
if d.warehouse and ((not cint(self.is_return) and self.docstatus==1)
|
||||||
|
or (cint(self.is_return) and self.docstatus==2)):
|
||||||
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
|
"actual_qty": -1*flt(d.qty),
|
||||||
|
"incoming_rate": return_rate
|
||||||
|
}))
|
||||||
|
|
||||||
|
if d.target_warehouse:
|
||||||
|
target_warehouse_sle = self.get_sl_entries(d, {
|
||||||
|
"actual_qty": flt(d.qty),
|
||||||
|
"warehouse": d.target_warehouse
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.docstatus == 1:
|
||||||
|
if not cint(self.is_return):
|
||||||
|
args = frappe._dict({
|
||||||
|
"item_code": d.item_code,
|
||||||
|
"warehouse": d.warehouse,
|
||||||
|
"posting_date": self.posting_date,
|
||||||
|
"posting_time": self.posting_time,
|
||||||
|
"qty": -1*flt(d.qty),
|
||||||
|
"serial_no": d.serial_no
|
||||||
|
})
|
||||||
|
target_warehouse_sle.update({
|
||||||
|
"incoming_rate": get_incoming_rate(args)
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
target_warehouse_sle.update({
|
||||||
|
"outgoing_rate": return_rate
|
||||||
|
})
|
||||||
|
sl_entries.append(target_warehouse_sle)
|
||||||
|
|
||||||
|
if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
|
||||||
|
or (cint(self.is_return) and self.docstatus==1)):
|
||||||
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
|
"actual_qty": -1*flt(d.qty),
|
||||||
|
"incoming_rate": return_rate
|
||||||
|
}))
|
||||||
|
|
||||||
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def check_active_sales_items(obj):
|
def check_active_sales_items(obj):
|
||||||
for d in obj.get("items"):
|
for d in obj.get("items"):
|
||||||
if d.item_code:
|
if d.item_code:
|
||||||
|
|||||||
@ -8,8 +8,6 @@ from frappe import msgprint, _
|
|||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||||
from erpnext.stock.utils import get_incoming_rate
|
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
class StockController(AccountsController):
|
class StockController(AccountsController):
|
||||||
@ -169,7 +167,7 @@ class StockController(AccountsController):
|
|||||||
else:
|
else:
|
||||||
is_expense_account = frappe.db.get_value("Account",
|
is_expense_account = frappe.db.get_value("Account",
|
||||||
item.get("expense_account"), "report_type")=="Profit and Loss"
|
item.get("expense_account"), "report_type")=="Profit and Loss"
|
||||||
if self.doctype not in ("Purchase Receipt", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
|
if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
|
||||||
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
|
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
|
||||||
.format(item.get("expense_account")))
|
.format(item.get("expense_account")))
|
||||||
if is_expense_account and not item.get("cost_center"):
|
if is_expense_account and not item.get("cost_center"):
|
||||||
@ -231,79 +229,6 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
return incoming_rate
|
return incoming_rate
|
||||||
|
|
||||||
def update_reserved_qty(self):
|
|
||||||
so_map = {}
|
|
||||||
for d in self.get("items"):
|
|
||||||
if d.so_detail:
|
|
||||||
if self.doctype == "Delivery Note" and d.against_sales_order:
|
|
||||||
so_map.setdefault(d.against_sales_order, []).append(d.so_detail)
|
|
||||||
elif self.doctype == "Sales Invoice" and d.sales_order and self.update_stock:
|
|
||||||
so_map.setdefault(d.sales_order, []).append(d.so_detail)
|
|
||||||
|
|
||||||
for so, so_item_rows in so_map.items():
|
|
||||||
if so and so_item_rows:
|
|
||||||
sales_order = frappe.get_doc("Sales Order", so)
|
|
||||||
|
|
||||||
if sales_order.status in ["Closed", "Cancelled"]:
|
|
||||||
frappe.throw(_("{0} {1} is cancelled or closed").format(_("Sales Order"), so),
|
|
||||||
frappe.InvalidStatusError)
|
|
||||||
|
|
||||||
sales_order.update_reserved_qty(so_item_rows)
|
|
||||||
|
|
||||||
def update_stock_ledger(self):
|
|
||||||
self.update_reserved_qty()
|
|
||||||
|
|
||||||
sl_entries = []
|
|
||||||
for d in self.get_item_list():
|
|
||||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
|
|
||||||
return_rate = 0
|
|
||||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
|
||||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
|
||||||
|
|
||||||
# On cancellation or if return entry submission, make stock ledger entry for
|
|
||||||
# target warehouse first, to update serial no values properly
|
|
||||||
|
|
||||||
if d.warehouse and ((not cint(self.is_return) and self.docstatus==1)
|
|
||||||
or (cint(self.is_return) and self.docstatus==2)):
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
|
||||||
"actual_qty": -1*flt(d.qty),
|
|
||||||
"incoming_rate": return_rate
|
|
||||||
}))
|
|
||||||
|
|
||||||
if d.target_warehouse:
|
|
||||||
target_warehouse_sle = self.get_sl_entries(d, {
|
|
||||||
"actual_qty": flt(d.qty),
|
|
||||||
"warehouse": d.target_warehouse
|
|
||||||
})
|
|
||||||
|
|
||||||
if self.docstatus == 1:
|
|
||||||
if not cint(self.is_return):
|
|
||||||
args = frappe._dict({
|
|
||||||
"item_code": d.item_code,
|
|
||||||
"warehouse": d.warehouse,
|
|
||||||
"posting_date": self.posting_date,
|
|
||||||
"posting_time": self.posting_time,
|
|
||||||
"qty": -1*flt(d.qty),
|
|
||||||
"serial_no": d.serial_no
|
|
||||||
})
|
|
||||||
target_warehouse_sle.update({
|
|
||||||
"incoming_rate": get_incoming_rate(args)
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
target_warehouse_sle.update({
|
|
||||||
"outgoing_rate": return_rate
|
|
||||||
})
|
|
||||||
sl_entries.append(target_warehouse_sle)
|
|
||||||
|
|
||||||
if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
|
|
||||||
or (cint(self.is_return) and self.docstatus==1)):
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
|
||||||
"actual_qty": -1*flt(d.qty),
|
|
||||||
"incoming_rate": return_rate
|
|
||||||
}))
|
|
||||||
|
|
||||||
self.make_sl_entries(sl_entries)
|
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self):
|
||||||
from erpnext.stock.utils import validate_warehouse_company
|
from erpnext.stock.utils import validate_warehouse_company
|
||||||
|
|
||||||
|
|||||||
@ -196,7 +196,7 @@ execute:frappe.db.sql("update `tabProduction Order` pro set description = (selec
|
|||||||
erpnext.patches.v5_7.item_template_attributes
|
erpnext.patches.v5_7.item_template_attributes
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants")
|
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants")
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item")
|
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item")
|
||||||
erpnext.patches.v4_2.repost_reserved_qty #2015-08-20
|
erpnext.patches.v4_2.repost_reserved_qty #2016-04-15
|
||||||
erpnext.patches.v5_4.update_purchase_cost_against_project
|
erpnext.patches.v5_4.update_purchase_cost_against_project
|
||||||
erpnext.patches.v5_8.update_order_reference_in_return_entries
|
erpnext.patches.v5_8.update_order_reference_in_return_entries
|
||||||
erpnext.patches.v5_8.add_credit_note_print_heading
|
erpnext.patches.v5_8.add_credit_note_print_heading
|
||||||
|
|||||||
@ -516,10 +516,10 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.frm.doc.doctype == "Sales Invoice") {
|
if(this.frm.doc.doctype == "Sales Invoice" || this.frm.doc.doctype == "Purchase Invoice") {
|
||||||
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
|
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
|
||||||
|
|
||||||
if(this.frm.doc.is_pos) {
|
if(this.frm.doc.is_pos || this.frm.doc.is_paid) {
|
||||||
if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) {
|
if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) {
|
||||||
this.frm.doc.paid_amount = flt(total_amount_to_pay);
|
this.frm.doc.paid_amount = flt(total_amount_to_pay);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,6 +127,34 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
dn.cancel()
|
dn.cancel()
|
||||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 10)
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 10)
|
||||||
|
|
||||||
|
def test_reserved_qty_for_over_delivery_via_sales_invoice(self):
|
||||||
|
# set over-delivery tolerance
|
||||||
|
frappe.db.set_value('Item', "_Test Item", 'tolerance', 50)
|
||||||
|
|
||||||
|
existing_reserved_qty = get_reserved_qty()
|
||||||
|
|
||||||
|
so = make_sales_order()
|
||||||
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 10)
|
||||||
|
|
||||||
|
si = make_sales_invoice(so.name)
|
||||||
|
si.update_stock = 1
|
||||||
|
si.get("items")[0].qty = 12
|
||||||
|
si.insert()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 12)
|
||||||
|
self.assertEqual(so.per_delivered, 100)
|
||||||
|
|
||||||
|
si.cancel()
|
||||||
|
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 10)
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 0)
|
||||||
|
self.assertEqual(so.per_delivered, 0)
|
||||||
|
|
||||||
def test_reserved_qty_for_partial_delivery_with_packing_list(self):
|
def test_reserved_qty_for_partial_delivery_with_packing_list(self):
|
||||||
existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
|
existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
|
||||||
existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
|
existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
|
||||||
|
|||||||
@ -191,6 +191,8 @@ class DeliveryNote(SellingController):
|
|||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.check_credit_limit()
|
self.check_credit_limit()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
@ -201,6 +203,8 @@ class DeliveryNote(SellingController):
|
|||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
self.update_billing_status()
|
self.update_billing_status()
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.cancel_packing_slips()
|
self.cancel_packing_slips()
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
"custom": 0,
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"document_type": "Document",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -15,6 +16,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
@ -23,6 +25,7 @@
|
|||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
@ -39,6 +42,7 @@
|
|||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
@ -48,6 +52,7 @@
|
|||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"print_width": "300px",
|
"print_width": "300px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -61,18 +66,47 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "purchase_receipt",
|
"fieldname": "receipt_document_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Purchase Receipt",
|
"label": "Receipt Document Type",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Purchase Receipt",
|
"options": "Purchase Invoice\nPurchase Receipt",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "receipt_document",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Receipt Document",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "receipt_document_type",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -88,12 +122,14 @@
|
|||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -109,6 +145,7 @@
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Qty",
|
"label": "Qty",
|
||||||
@ -116,6 +153,7 @@
|
|||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -131,6 +169,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Rate",
|
"label": "Rate",
|
||||||
@ -139,6 +178,7 @@
|
|||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -154,6 +194,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
@ -164,6 +205,7 @@
|
|||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
@ -179,6 +221,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Applicable Charges",
|
"label": "Applicable Charges",
|
||||||
@ -187,6 +230,7 @@
|
|||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -202,6 +246,7 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Purchase Receipt Item",
|
"label": "Purchase Receipt Item",
|
||||||
@ -209,6 +254,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -226,12 +272,13 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2015-11-16 06:29:49.057949",
|
"modified": "2016-04-07 16:18:00.859492",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Item",
|
"name": "Landed Cost Item",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "wasim@webnotestech.com",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0
|
"read_only_onload": 0,
|
||||||
|
"track_seen": 0
|
||||||
}
|
}
|
||||||
@ -6,25 +6,54 @@
|
|||||||
"custom": 0,
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"document_type": "Document",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "purchase_receipt",
|
"fieldname": "receipt_document_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Receipt Document Type",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "\nPurchase Invoice\nPurchase Receipt",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "receipt_document",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Purchase Receipt",
|
"label": "Receipt Document",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "purchase_receipt_no",
|
"oldfieldname": "purchase_receipt_no",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Purchase Receipt",
|
"options": "receipt_document_type",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"print_width": "220px",
|
"print_width": "220px",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -42,6 +71,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Supplier",
|
"label": "Supplier",
|
||||||
@ -50,6 +80,7 @@
|
|||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -65,12 +96,14 @@
|
|||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -87,6 +120,7 @@
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
@ -94,6 +128,7 @@
|
|||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -109,6 +144,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Grand Total",
|
"label": "Grand Total",
|
||||||
@ -116,6 +152,7 @@
|
|||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -133,12 +170,13 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2015-11-16 06:29:49.106523",
|
"modified": "2016-04-07 15:14:56.955036",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Purchase Receipt",
|
"name": "Landed Cost Purchase Receipt",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "wasim@webnotestech.com",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0
|
"read_only_onload": 0,
|
||||||
|
"track_seen": 0
|
||||||
}
|
}
|
||||||
@ -8,20 +8,28 @@ frappe.require("assets/erpnext/js/controllers/stock_controller.js");
|
|||||||
erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||||
setup: function() {
|
setup: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
this.frm.fields_dict.purchase_receipts.grid.get_field('purchase_receipt').get_query =
|
this.frm.fields_dict.purchase_receipts.grid.get_field('receipt_document').get_query =
|
||||||
function() {
|
function(doc, cdt ,cdn) {
|
||||||
|
var d = locals[cdt][cdn]
|
||||||
|
|
||||||
|
var filters = [
|
||||||
|
[d.receipt_document_type, 'docstatus', '=', '1'],
|
||||||
|
[d.receipt_document_type, 'company', '=', me.frm.doc.company],
|
||||||
|
]
|
||||||
|
|
||||||
|
if(d.receipt_document_type == "Purchase Invoice") {
|
||||||
|
filters.push(["Purchase Invoice", "update_stock", "=", "1"])
|
||||||
|
}
|
||||||
|
|
||||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||||
return {
|
return {
|
||||||
filters:[
|
filters:filters
|
||||||
['Purchase Receipt', 'docstatus', '=', '1'],
|
|
||||||
['Purchase Receipt', 'company', '=', me.frm.doc.company],
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.frm.add_fetch("purchase_receipt", "supplier", "supplier");
|
this.frm.add_fetch("receipt_document", "supplier", "supplier");
|
||||||
this.frm.add_fetch("purchase_receipt", "posting_date", "posting_date");
|
this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
|
||||||
this.frm.add_fetch("purchase_receipt", "base_grand_total", "grand_total");
|
this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -14,9 +14,9 @@ class LandedCostVoucher(Document):
|
|||||||
for pr in self.get("purchase_receipts"):
|
for pr in self.get("purchase_receipts"):
|
||||||
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
|
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
|
||||||
pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name
|
pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name
|
||||||
from `tabPurchase Receipt Item` pr_item where parent = %s
|
from `tab{doctype} Item` pr_item where parent = %s
|
||||||
and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 1)""",
|
and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 1)
|
||||||
pr.purchase_receipt, as_dict=True)
|
""".format(doctype=pr.receipt_document_type), pr.receipt_document, as_dict=True)
|
||||||
|
|
||||||
for d in pr_items:
|
for d in pr_items:
|
||||||
item = self.append("items")
|
item = self.append("items")
|
||||||
@ -25,13 +25,13 @@ class LandedCostVoucher(Document):
|
|||||||
item.qty = d.qty
|
item.qty = d.qty
|
||||||
item.rate = d.base_rate
|
item.rate = d.base_rate
|
||||||
item.amount = d.base_amount
|
item.amount = d.base_amount
|
||||||
item.purchase_receipt = pr.purchase_receipt
|
item.receipt_document_type = pr.receipt_document_type
|
||||||
|
item.receipt_document = pr.receipt_document
|
||||||
item.purchase_receipt_item = d.name
|
item.purchase_receipt_item = d.name
|
||||||
|
|
||||||
if self.get("taxes"):
|
if self.get("taxes"):
|
||||||
self.set_applicable_charges_for_item()
|
self.set_applicable_charges_for_item()
|
||||||
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.check_mandatory()
|
self.check_mandatory()
|
||||||
self.validate_purchase_receipts()
|
self.validate_purchase_receipts()
|
||||||
@ -43,25 +43,26 @@ class LandedCostVoucher(Document):
|
|||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
if not self.get("purchase_receipts"):
|
if not self.get("purchase_receipts"):
|
||||||
frappe.throw(_("Please enter Purchase Receipts"))
|
frappe.throw(_("Please enter Receipt Document"))
|
||||||
|
|
||||||
if not self.get("taxes"):
|
if not self.get("taxes"):
|
||||||
frappe.throw(_("Please enter Taxes and Charges"))
|
frappe.throw(_("Please enter Taxes and Charges"))
|
||||||
|
|
||||||
def validate_purchase_receipts(self):
|
def validate_purchase_receipts(self):
|
||||||
purchase_receipts = []
|
receipt_documents = []
|
||||||
|
|
||||||
for d in self.get("purchase_receipts"):
|
for d in self.get("purchase_receipts"):
|
||||||
if frappe.db.get_value("Purchase Receipt", d.purchase_receipt, "docstatus") != 1:
|
if frappe.db.get_value(d.receipt_document_type, d.receipt_document, "docstatus") != 1:
|
||||||
frappe.throw(_("Purchase Receipt must be submitted"))
|
frappe.throw(_("Receipt document must be submitted"))
|
||||||
else:
|
else:
|
||||||
purchase_receipts.append(d.purchase_receipt)
|
receipt_documents.append(d.receipt_document)
|
||||||
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if not item.purchase_receipt:
|
if not item.receipt_document:
|
||||||
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
|
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
|
||||||
elif item.purchase_receipt not in purchase_receipts:
|
elif item.receipt_document not in receipt_documents:
|
||||||
frappe.throw(_("Item Row {0}: Purchase Receipt {1} does not exist in above 'Purchase Receipts' table")
|
frappe.throw(_("Item Row {idx}: {doctype} {docname} does not exist in above '{doctype}' table")
|
||||||
.format(item.idx, item.purchase_receipt))
|
.format(idx=item.idx, doctype=item.receipt_document_type, docname=item.receipt_document))
|
||||||
|
|
||||||
def set_total_taxes_and_charges(self):
|
def set_total_taxes_and_charges(self):
|
||||||
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
|
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
|
||||||
@ -83,36 +84,35 @@ class LandedCostVoucher(Document):
|
|||||||
self.update_landed_cost()
|
self.update_landed_cost()
|
||||||
|
|
||||||
def update_landed_cost(self):
|
def update_landed_cost(self):
|
||||||
purchase_receipts = list(set([d.purchase_receipt for d in self.get("items")]))
|
for d in self.get("items"):
|
||||||
for purchase_receipt in purchase_receipts:
|
doc = frappe.get_doc(d.receipt_document_type, d.receipt_document)
|
||||||
pr = frappe.get_doc("Purchase Receipt", purchase_receipt)
|
|
||||||
|
|
||||||
# set landed cost voucher amount in pr item
|
# set landed cost voucher amount in pr item
|
||||||
pr.set_landed_cost_voucher_amount()
|
doc.set_landed_cost_voucher_amount()
|
||||||
|
|
||||||
# set valuation amount in pr item
|
# set valuation amount in pr item
|
||||||
pr.update_valuation_rate("items")
|
doc.update_valuation_rate("items")
|
||||||
|
|
||||||
# save will update landed_cost_voucher_amount and voucher_amount in PR,
|
# save will update landed_cost_voucher_amount and voucher_amount in PR,
|
||||||
# as those fields are allowed to edit after submit
|
# as those fields are allowed to edit after submit
|
||||||
pr.save()
|
doc.save()
|
||||||
|
|
||||||
# update latest valuation rate in serial no
|
# update latest valuation rate in serial no
|
||||||
self.update_rate_in_serial_no(pr)
|
self.update_rate_in_serial_no(doc)
|
||||||
|
|
||||||
# update stock & gl entries for cancelled state of PR
|
# update stock & gl entries for cancelled state of PR
|
||||||
pr.docstatus = 2
|
doc.docstatus = 2
|
||||||
pr.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
||||||
pr.make_gl_entries_on_cancel()
|
doc.make_gl_entries_on_cancel()
|
||||||
|
|
||||||
|
|
||||||
# update stock & gl entries for submit state of PR
|
# update stock & gl entries for submit state of PR
|
||||||
pr.docstatus = 1
|
doc.docstatus = 1
|
||||||
pr.update_stock_ledger(via_landed_cost_voucher=True)
|
doc.update_stock_ledger(via_landed_cost_voucher=True)
|
||||||
pr.make_gl_entries()
|
doc.make_gl_entries()
|
||||||
|
|
||||||
def update_rate_in_serial_no(self, purchase_receipt):
|
def update_rate_in_serial_no(self, receipt_document):
|
||||||
for item in purchase_receipt.get("items"):
|
for item in receipt_document.get("items"):
|
||||||
if item.serial_no:
|
if item.serial_no:
|
||||||
serial_nos = get_serial_nos(item.serial_no)
|
serial_nos = get_serial_nos(item.serial_no)
|
||||||
if serial_nos:
|
if serial_nos:
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||||
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records
|
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
|
|
||||||
class TestLandedCostVoucher(unittest.TestCase):
|
class TestLandedCostVoucher(unittest.TestCase):
|
||||||
def test_landed_cost_voucher(self):
|
def test_landed_cost_voucher(self):
|
||||||
@ -21,10 +21,9 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"warehouse": "_Test Warehouse - _TC"
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
},
|
},
|
||||||
fieldname=["qty_after_transaction", "stock_value"],
|
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
|
||||||
as_dict=1)
|
|
||||||
|
|
||||||
self.submit_landed_cost_voucher(pr)
|
self.submit_landed_cost_voucher("Purchase Receipt", pr.name)
|
||||||
|
|
||||||
pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
|
pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
|
||||||
self.assertEquals(pr_lc_value, 25.0)
|
self.assertEquals(pr_lc_value, 25.0)
|
||||||
@ -35,8 +34,7 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"warehouse": "_Test Warehouse - _TC"
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
},
|
},
|
||||||
fieldname=["qty_after_transaction", "stock_value"],
|
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
|
||||||
as_dict=1)
|
|
||||||
|
|
||||||
self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
|
self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
|
||||||
|
|
||||||
@ -63,6 +61,55 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
|
def test_landed_cost_voucher_against_purchase_invoice(self):
|
||||||
|
set_perpetual_inventory(1)
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
|
posting_time=frappe.utils.nowtime())
|
||||||
|
|
||||||
|
last_sle = frappe.db.get_value("Stock Ledger Entry", {
|
||||||
|
"voucher_type": pi.doctype,
|
||||||
|
"voucher_no": pi.name,
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
|
},
|
||||||
|
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
|
||||||
|
|
||||||
|
self.submit_landed_cost_voucher("Purchase Invoice", pi.name)
|
||||||
|
|
||||||
|
pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name},
|
||||||
|
"landed_cost_voucher_amount")
|
||||||
|
|
||||||
|
self.assertEquals(pi_lc_value, 50.0)
|
||||||
|
|
||||||
|
last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", {
|
||||||
|
"voucher_type": pi.doctype,
|
||||||
|
"voucher_no": pi.name,
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
|
},
|
||||||
|
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
|
||||||
|
|
||||||
|
self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
|
||||||
|
|
||||||
|
self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
|
||||||
|
|
||||||
|
gl_entries = get_gl_entries("Purchase Invoice", pi.name)
|
||||||
|
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
expected_values = {
|
||||||
|
pi.get("items")[0].warehouse: [300.0, 0.0],
|
||||||
|
"Creditors - _TC": [0.0, 250.0],
|
||||||
|
"Expenses Included In Valuation - _TC": [0.0, 50.0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for gle in gl_entries:
|
||||||
|
self.assertEquals(expected_values[gle.account][0], gle.debit)
|
||||||
|
self.assertEquals(expected_values[gle.account][1], gle.credit)
|
||||||
|
|
||||||
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_landed_cost_voucher_for_serialized_item(self):
|
def test_landed_cost_voucher_for_serialized_item(self):
|
||||||
set_perpetual_inventory(1)
|
set_perpetual_inventory(1)
|
||||||
frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
|
frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
|
||||||
@ -74,7 +121,7 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
|
|
||||||
serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate")
|
serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate")
|
||||||
|
|
||||||
self.submit_landed_cost_voucher(pr)
|
self.submit_landed_cost_voucher("Purchase Receipt", pr.name)
|
||||||
|
|
||||||
serial_no = frappe.db.get_value("Serial No", "SN001",
|
serial_no = frappe.db.get_value("Serial No", "SN001",
|
||||||
["warehouse", "purchase_rate"], as_dict=1)
|
["warehouse", "purchase_rate"], as_dict=1)
|
||||||
@ -84,19 +131,23 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def submit_landed_cost_voucher(self, pr):
|
def submit_landed_cost_voucher(self, receipt_document_type, receipt_document):
|
||||||
|
ref_doc = frappe.get_doc(receipt_document_type, receipt_document)
|
||||||
|
|
||||||
lcv = frappe.new_doc("Landed Cost Voucher")
|
lcv = frappe.new_doc("Landed Cost Voucher")
|
||||||
lcv.company = "_Test Company"
|
lcv.company = "_Test Company"
|
||||||
lcv.set("purchase_receipts", [{
|
lcv.set("purchase_receipts", [{
|
||||||
"purchase_receipt": pr.name,
|
"receipt_document_type": receipt_document_type,
|
||||||
"supplier": pr.supplier,
|
"receipt_document": receipt_document,
|
||||||
"posting_date": pr.posting_date,
|
"supplier": ref_doc.supplier,
|
||||||
"grand_total": pr.base_grand_total
|
"posting_date": ref_doc.posting_date,
|
||||||
|
"grand_total": ref_doc.base_grand_total
|
||||||
}])
|
}])
|
||||||
|
|
||||||
lcv.set("taxes", [{
|
lcv.set("taxes", [{
|
||||||
"description": "Insurance Charges",
|
"description": "Insurance Charges",
|
||||||
"account": "_Test Account Insurance Charges - _TC",
|
"account": "_Test Account Insurance Charges - _TC",
|
||||||
"amount": 50.0
|
"amount": 50
|
||||||
}])
|
}])
|
||||||
|
|
||||||
lcv.insert()
|
lcv.insert()
|
||||||
|
|||||||
@ -21,10 +21,23 @@ frappe.ui.form.on("Purchase Receipt", {
|
|||||||
"batch_no": doc.batch_no
|
"batch_no": doc.batch_no
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$.each(["warehouse", "rejected_warehouse"], function(i, field) {
|
||||||
|
frm.set_query(field, "items", function() {
|
||||||
|
return {
|
||||||
|
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("supplier_warehouse", function() {
|
||||||
|
return {
|
||||||
|
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]]
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
|
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
this._super();
|
this._super();
|
||||||
@ -76,48 +89,6 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
|
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
|
||||||
},
|
},
|
||||||
|
|
||||||
received_qty: function(doc, cdt, cdn) {
|
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
|
||||||
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
|
||||||
|
|
||||||
item.qty = (item.qty < item.received_qty) ? item.qty : item.received_qty;
|
|
||||||
this.qty(doc, cdt, cdn);
|
|
||||||
},
|
|
||||||
|
|
||||||
qty: function(doc, cdt, cdn) {
|
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
|
||||||
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
|
||||||
|
|
||||||
if(!(item.received_qty || item.rejected_qty) && item.qty) {
|
|
||||||
item.received_qty = item.qty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(item.qty > item.received_qty) {
|
|
||||||
msgprint(__("Error: {0} > {1}", [__(frappe.meta.get_label(item.doctype, "qty", item.name)),
|
|
||||||
__(frappe.meta.get_label(item.doctype, "received_qty", item.name))]))
|
|
||||||
item.qty = item.rejected_qty = 0.0;
|
|
||||||
} else {
|
|
||||||
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._super(doc, cdt, cdn);
|
|
||||||
},
|
|
||||||
|
|
||||||
rejected_qty: function(doc, cdt, cdn) {
|
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
|
||||||
frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
|
|
||||||
|
|
||||||
if(item.rejected_qty > item.received_qty) {
|
|
||||||
msgprint(__("Error: {0} > {1}", [__(frappe.meta.get_label(item.doctype, "rejected_qty", item.name)),
|
|
||||||
__(frappe.meta.get_label(item.doctype, "received_qty", item.name))]));
|
|
||||||
item.qty = item.rejected_qty = 0.0;
|
|
||||||
} else {
|
|
||||||
item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.qty(doc, cdt, cdn);
|
|
||||||
},
|
|
||||||
|
|
||||||
make_purchase_invoice: function() {
|
make_purchase_invoice: function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
|
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
|
||||||
@ -132,10 +103,6 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
tc_name: function() {
|
|
||||||
this.get_terms();
|
|
||||||
},
|
|
||||||
|
|
||||||
close_purchase_receipt: function() {
|
close_purchase_receipt: function() {
|
||||||
cur_frm.cscript.update_status("Closed");
|
cur_frm.cscript.update_status("Closed");
|
||||||
},
|
},
|
||||||
@ -242,8 +209,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
|||||||
cur_frm.email_doc(frappe.boot.notification_settings.purchase_receipt_message);
|
cur_frm.email_doc(frappe.boot.notification_settings.purchase_receipt_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
frappe.provide("erpnext.buying");
|
frappe.provide("erpnext.buying");
|
||||||
|
|
||||||
frappe.ui.form.on("Purchase Receipt", "is_subcontracted", function(frm) {
|
frappe.ui.form.on("Purchase Receipt", "is_subcontracted", function(frm) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cstr, flt, cint
|
from frappe.utils import flt, cint
|
||||||
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
@ -52,62 +52,13 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
self.po_required()
|
self.po_required()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_purchase_return()
|
|
||||||
self.validate_rejected_warehouse()
|
|
||||||
self.validate_accepted_rejected_qty()
|
|
||||||
self.validate_inspection()
|
self.validate_inspection()
|
||||||
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
|
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
|
||||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||||
|
|
||||||
pc_obj = frappe.get_doc('Purchase Common')
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
pc_obj.validate_for_items(self)
|
|
||||||
self.check_for_closed_status(pc_obj)
|
self.check_for_closed_status(pc_obj)
|
||||||
|
|
||||||
# sub-contracting
|
|
||||||
self.validate_for_subcontracting()
|
|
||||||
self.create_raw_materials_supplied("supplied_items")
|
|
||||||
self.set_landed_cost_voucher_amount()
|
|
||||||
self.update_valuation_rate("items")
|
|
||||||
|
|
||||||
|
|
||||||
def set_landed_cost_voucher_amount(self):
|
|
||||||
for d in self.get("items"):
|
|
||||||
lc_voucher_amount = frappe.db.sql("""select sum(applicable_charges)
|
|
||||||
from `tabLanded Cost Item`
|
|
||||||
where docstatus = 1 and purchase_receipt_item = %s""", d.name)
|
|
||||||
d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0
|
|
||||||
|
|
||||||
def validate_purchase_return(self):
|
|
||||||
for d in self.get("items"):
|
|
||||||
if self.is_return and flt(d.rejected_qty) != 0:
|
|
||||||
frappe.throw(_("Row #{0}: Rejected Qty can not be entered in Purchase Return").format(d.idx))
|
|
||||||
|
|
||||||
# validate rate with ref PR
|
|
||||||
|
|
||||||
def validate_rejected_warehouse(self):
|
|
||||||
for d in self.get("items"):
|
|
||||||
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
|
||||||
d.rejected_warehouse = self.rejected_warehouse
|
|
||||||
if not d.rejected_warehouse:
|
|
||||||
frappe.throw(_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(d.idx, d.item_code))
|
|
||||||
|
|
||||||
# validate accepted and rejected qty
|
|
||||||
def validate_accepted_rejected_qty(self):
|
|
||||||
for d in self.get("items"):
|
|
||||||
if not flt(d.received_qty) and flt(d.qty):
|
|
||||||
d.received_qty = flt(d.qty) - flt(d.rejected_qty)
|
|
||||||
|
|
||||||
elif not flt(d.qty) and flt(d.rejected_qty):
|
|
||||||
d.qty = flt(d.received_qty) - flt(d.rejected_qty)
|
|
||||||
|
|
||||||
elif not flt(d.rejected_qty):
|
|
||||||
d.rejected_qty = flt(d.received_qty) - flt(d.qty)
|
|
||||||
|
|
||||||
# Check Received Qty = Accepted Qty + Rejected Qty
|
|
||||||
if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)):
|
|
||||||
frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_with_previous_doc(self):
|
def validate_with_previous_doc(self):
|
||||||
super(PurchaseReceipt, self).validate_with_previous_doc({
|
super(PurchaseReceipt, self).validate_with_previous_doc({
|
||||||
"Purchase Order": {
|
"Purchase Order": {
|
||||||
@ -130,59 +81,6 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if not d.prevdoc_docname:
|
if not d.prevdoc_docname:
|
||||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
|
|
||||||
sl_entries = []
|
|
||||||
stock_items = self.get_stock_items()
|
|
||||||
|
|
||||||
for d in self.get('items'):
|
|
||||||
if d.item_code in stock_items and d.warehouse:
|
|
||||||
pr_qty = flt(d.qty) * flt(d.conversion_factor)
|
|
||||||
|
|
||||||
if pr_qty:
|
|
||||||
val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9
|
|
||||||
rate = flt(d.valuation_rate, val_rate_db_precision)
|
|
||||||
sle = self.get_sl_entries(d, {
|
|
||||||
"actual_qty": flt(pr_qty),
|
|
||||||
"serial_no": cstr(d.serial_no).strip()
|
|
||||||
})
|
|
||||||
if self.is_return:
|
|
||||||
sle.update({
|
|
||||||
"outgoing_rate": rate
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
sle.update({
|
|
||||||
"incoming_rate": rate
|
|
||||||
})
|
|
||||||
sl_entries.append(sle)
|
|
||||||
|
|
||||||
if flt(d.rejected_qty) > 0:
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
|
||||||
"warehouse": d.rejected_warehouse,
|
|
||||||
"actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
|
|
||||||
"serial_no": cstr(d.rejected_serial_no).strip(),
|
|
||||||
"incoming_rate": 0.0
|
|
||||||
}))
|
|
||||||
|
|
||||||
self.bk_flush_supp_wh(sl_entries)
|
|
||||||
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
|
|
||||||
via_landed_cost_voucher=via_landed_cost_voucher)
|
|
||||||
|
|
||||||
def update_ordered_qty(self):
|
|
||||||
po_map = {}
|
|
||||||
for d in self.get("items"):
|
|
||||||
if d.prevdoc_doctype and d.prevdoc_doctype == "Purchase Order" and d.prevdoc_detail_docname:
|
|
||||||
po_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
|
||||||
|
|
||||||
for po, po_item_rows in po_map.items():
|
|
||||||
if po and po_item_rows:
|
|
||||||
po_obj = frappe.get_doc("Purchase Order", po)
|
|
||||||
|
|
||||||
if po_obj.status in ["Closed", "Cancelled"]:
|
|
||||||
frappe.throw(_("{0} {1} is cancelled or closed").format(_("Purchase Order"), po),
|
|
||||||
frappe.InvalidStatusError)
|
|
||||||
|
|
||||||
po_obj.update_ordered_qty(po_item_rows)
|
|
||||||
|
|
||||||
def get_already_received_qty(self, po, po_detail):
|
def get_already_received_qty(self, po, po_detail):
|
||||||
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
||||||
where prevdoc_detail_docname = %s and docstatus = 1
|
where prevdoc_detail_docname = %s and docstatus = 1
|
||||||
@ -195,16 +93,6 @@ class PurchaseReceipt(BuyingController):
|
|||||||
["qty", "warehouse"])
|
["qty", "warehouse"])
|
||||||
return po_qty, po_warehouse
|
return po_qty, po_warehouse
|
||||||
|
|
||||||
def bk_flush_supp_wh(self, sl_entries):
|
|
||||||
for d in self.get('supplied_items'):
|
|
||||||
# negative quantity is passed as raw material qty has to be decreased
|
|
||||||
# when PR is submitted and it has to be increased when PR is cancelled
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
|
||||||
"item_code": d.rm_item_code,
|
|
||||||
"warehouse": self.supplier_warehouse,
|
|
||||||
"actual_qty": -1*flt(d.consumed_qty),
|
|
||||||
}))
|
|
||||||
|
|
||||||
def validate_inspection(self):
|
def validate_inspection(self):
|
||||||
for d in self.get('items'): #Enter inspection date for all items that require inspection
|
for d in self.get('items'): #Enter inspection date for all items that require inspection
|
||||||
if frappe.db.get_value("Item", d.item_code, "inspection_required") and not d.qa_no:
|
if frappe.db.get_value("Item", d.item_code, "inspection_required") and not d.qa_no:
|
||||||
@ -225,19 +113,20 @@ class PurchaseReceipt(BuyingController):
|
|||||||
purchase_controller = frappe.get_doc("Purchase Common")
|
purchase_controller = frappe.get_doc("Purchase Common")
|
||||||
|
|
||||||
# Check for Approving Authority
|
# Check for Approving Authority
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total)
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
|
self.company, self.base_grand_total)
|
||||||
|
|
||||||
# Set status as Submitted
|
# Set status as Submitted
|
||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
self.update_ordered_qty()
|
|
||||||
|
|
||||||
self.update_billing_status()
|
self.update_billing_status()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
purchase_controller.update_last_purchase_rate(self, 1)
|
purchase_controller.update_last_purchase_rate(self, 1)
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||||
@ -267,17 +156,15 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
frappe.db.set(self,'status','Cancelled')
|
frappe.db.set(self,'status','Cancelled')
|
||||||
|
|
||||||
self.update_stock_ledger()
|
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
# Must be called after updating received qty in PO
|
|
||||||
self.update_ordered_qty()
|
|
||||||
|
|
||||||
self.update_billing_status()
|
self.update_billing_status()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
pc_obj.update_last_purchase_rate(self, 0)
|
pc_obj.update_last_purchase_rate(self, 0)
|
||||||
|
|
||||||
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
|
self.update_stock_ledger()
|
||||||
self.make_gl_entries_on_cancel()
|
self.make_gl_entries_on_cancel()
|
||||||
|
|
||||||
def get_current_stock(self):
|
def get_current_stock(self):
|
||||||
@ -352,13 +239,11 @@ class PurchaseReceipt(BuyingController):
|
|||||||
}, warehouse_account[self.supplier_warehouse]["account_currency"]))
|
}, warehouse_account[self.supplier_warehouse]["account_currency"]))
|
||||||
|
|
||||||
# divisional loss adjustment
|
# divisional loss adjustment
|
||||||
sle_valuation_amount = flt(flt(d.valuation_rate, val_rate_db_precision) * flt(d.qty) * flt(d.conversion_factor),
|
distributed_amount = flt(flt(d.base_net_amount, d.precision("base_net_amount"))) + \
|
||||||
self.precision("base_net_amount", d))
|
|
||||||
|
|
||||||
distributed_amount = flt(flt(d.base_net_amount, self.precision("base_net_amount", d))) + \
|
|
||||||
flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost) + flt(d.item_tax_amount)
|
flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost) + flt(d.item_tax_amount)
|
||||||
|
|
||||||
divisional_loss = flt(distributed_amount - sle_valuation_amount, self.precision("base_net_amount", d))
|
divisional_loss = flt(distributed_amount - stock_value_diff,
|
||||||
|
d.precision("base_net_amount"))
|
||||||
if divisional_loss:
|
if divisional_loss:
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": stock_rbnb,
|
"account": stock_rbnb,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user