Cleanup and test cases for serialized item
This commit is contained in:
parent
f061877b4f
commit
3cf67a462b
@ -21,12 +21,10 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
|
|
||||||
// Show / Hide button
|
// Show / Hide button
|
||||||
if(doc.docstatus==1 && doc.outstanding_amount > 0)
|
if(doc.docstatus==1 && doc.outstanding_amount > 0)
|
||||||
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry,
|
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry);
|
||||||
frappe.boot.doctype_icons["Journal Entry"]);
|
|
||||||
|
|
||||||
if(doc.docstatus==1) {
|
if(doc.docstatus==1) {
|
||||||
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return,
|
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return);
|
||||||
frappe.boot.doctype_icons["Purchase Invoice"]);
|
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('View Ledger'), function() {
|
cur_frm.add_custom_button(__('View Ledger'), function() {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
@ -37,7 +35,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
group_by_voucher: 0
|
group_by_voucher: 0
|
||||||
};
|
};
|
||||||
frappe.set_route("query-report", "General Ledger");
|
frappe.set_route("query-report", "General Ledger");
|
||||||
}, "icon-table");
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus===0) {
|
if(doc.docstatus===0) {
|
||||||
@ -54,7 +52,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default");
|
});
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('From Purchase Receipt'),
|
cur_frm.add_custom_button(__('From Purchase Receipt'),
|
||||||
function() {
|
function() {
|
||||||
@ -67,7 +65,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default");
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -158,7 +158,7 @@
|
|||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return",
|
"label": "Is Return",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -169,7 +169,7 @@
|
|||||||
"fieldname": "return_against",
|
"fieldname": "return_against",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Return Against Purchase Invoice",
|
"label": "Return Against Purchase Invoice",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"options": "Purchase Invoice",
|
"options": "Purchase Invoice",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
@ -962,7 +962,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-07-17 14:09:19.666457",
|
"modified": "2015-07-24 11:49:59.762109",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -412,5 +412,5 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_return(source_name, target_doc=None):
|
def make_purchase_return(source_name, target_doc=None):
|
||||||
from erpnext.utilities.transaction_base import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
return make_return_doc("Purchase Invoice", source_name, target_doc)
|
return make_return_doc("Purchase Invoice", source_name, target_doc)
|
@ -53,7 +53,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
group_by_voucher: 0
|
group_by_voucher: 0
|
||||||
};
|
};
|
||||||
frappe.set_route("query-report", "General Ledger");
|
frappe.set_route("query-report", "General Ledger");
|
||||||
}, "icon-table");
|
});
|
||||||
|
|
||||||
if(cint(doc.update_stock)!=1) {
|
if(cint(doc.update_stock)!=1) {
|
||||||
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
||||||
@ -64,16 +64,15 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(!from_delivery_note) {
|
if(!from_delivery_note) {
|
||||||
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'], "icon-truck")
|
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
||||||
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry, "icon-money");
|
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return,
|
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return);
|
||||||
frappe.boot.doctype_icons["Sales Invoice"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show buttons only when pos view is active
|
// Show buttons only when pos view is active
|
||||||
|
@ -173,7 +173,7 @@
|
|||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return",
|
"label": "Is Return",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -184,7 +184,7 @@
|
|||||||
"fieldname": "return_against",
|
"fieldname": "return_against",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Return Against Sales Invoice",
|
"label": "Return Against Sales Invoice",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"options": "Sales Invoice",
|
"options": "Sales Invoice",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
@ -1275,7 +1275,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-07-22 16:53:52.995407",
|
"modified": "2015-07-24 11:48:07.544569",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -663,5 +663,5 @@ def make_delivery_note(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_return(source_name, target_doc=None):
|
def make_sales_return(source_name, target_doc=None):
|
||||||
from erpnext.utilities.transaction_base import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
return make_return_doc("Sales Invoice", source_name, target_doc)
|
return make_return_doc("Sales Invoice", source_name, target_doc)
|
@ -11,39 +11,32 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
this._super();
|
this._super();
|
||||||
// this.frm.dashboard.reset();
|
// this.frm.dashboard.reset();
|
||||||
|
|
||||||
if(doc.docstatus == 1 && doc.status != 'Stopped'){
|
if(doc.docstatus == 1 && doc.status != 'Stopped') {
|
||||||
// cur_frm.dashboard.add_progress(cint(doc.per_received) + __("% Received"),
|
|
||||||
// doc.per_received);
|
|
||||||
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
|
||||||
// doc.per_billed);
|
|
||||||
|
|
||||||
if(flt(doc.per_received, 2) < 100) {
|
if(flt(doc.per_received, 2) < 100) {
|
||||||
cur_frm.add_custom_button(__('Make Purchase Receipt'),
|
cur_frm.add_custom_button(__('Make Purchase Receipt'), this.make_purchase_receipt);
|
||||||
this.make_purchase_receipt);
|
|
||||||
if(doc.is_subcontracted==="Yes") {
|
if(doc.is_subcontracted==="Yes") {
|
||||||
cur_frm.add_custom_button(__('Transfer Material to Supplier'),
|
cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry);
|
||||||
function() { me.make_stock_entry() });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(flt(doc.per_billed, 2) < 100)
|
if(flt(doc.per_billed, 2) < 100)
|
||||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice,
|
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice);
|
||||||
frappe.boot.doctype_icons["Purchase Invoice"]);
|
|
||||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order'],
|
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
||||||
"icon-exclamation", "btn-default");
|
|
||||||
|
|
||||||
} else if(doc.docstatus===0) {
|
} else if(doc.docstatus===0) {
|
||||||
cur_frm.cscript.add_from_mappers();
|
cur_frm.cscript.add_from_mappers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
||||||
cur_frm.add_custom_button(__('Unstop Purchase Order'),
|
cur_frm.add_custom_button(__('Unstop Purchase Order'), cur_frm.cscript['Unstop Purchase Order']);
|
||||||
cur_frm.cscript['Unstop Purchase Order'], "icon-check");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
make_stock_entry: function() {
|
make_stock_entry: function() {
|
||||||
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }),
|
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }),
|
||||||
me = this;
|
var me = this;
|
||||||
|
|
||||||
if(items.length===1) {
|
if(items.length===1) {
|
||||||
me._make_stock_entry(items[0]);
|
me._make_stock_entry(items[0]);
|
||||||
return;
|
return;
|
||||||
@ -96,7 +89,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default"
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('From Supplier Quotation'),
|
cur_frm.add_custom_button(__('From Supplier Quotation'),
|
||||||
@ -110,7 +103,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default"
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('For Supplier'),
|
cur_frm.add_custom_button(__('For Supplier'),
|
||||||
@ -122,7 +115,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
docstatus: ["!=", 2],
|
docstatus: ["!=", 2],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default"
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4,14 +4,12 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, throw
|
from frappe import _, throw
|
||||||
from frappe.utils import today, flt, cint, format_datetime, get_datetime
|
from frappe.utils import today, flt, cint
|
||||||
from erpnext.setup.utils import get_company_currency, get_exchange_rate
|
from erpnext.setup.utils import get_company_currency, get_exchange_rate
|
||||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import validate_return
|
||||||
class StockOverReturnError(frappe.ValidationError): pass
|
|
||||||
|
|
||||||
|
|
||||||
class AccountsController(TransactionBase):
|
class AccountsController(TransactionBase):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@ -23,7 +21,7 @@ class AccountsController(TransactionBase):
|
|||||||
if not self.meta.get_field("is_return") or not self.is_return:
|
if not self.meta.get_field("is_return") or not self.is_return:
|
||||||
self.validate_value("base_grand_total", ">=", 0)
|
self.validate_value("base_grand_total", ">=", 0)
|
||||||
|
|
||||||
self.validate_return_doc()
|
validate_return(self)
|
||||||
self.set_total_in_words()
|
self.set_total_in_words()
|
||||||
|
|
||||||
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
|
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
|
||||||
@ -58,98 +56,6 @@ class AccountsController(TransactionBase):
|
|||||||
self.fiscal_year = get_fiscal_year(self.get(fieldname))[0]
|
self.fiscal_year = get_fiscal_year(self.get(fieldname))[0]
|
||||||
break
|
break
|
||||||
|
|
||||||
def validate_return_doc(self):
|
|
||||||
if not self.meta.get_field("is_return") or not self.is_return:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.validate_return_against()
|
|
||||||
self.validate_returned_items()
|
|
||||||
|
|
||||||
def validate_return_against(self):
|
|
||||||
if not self.return_against:
|
|
||||||
frappe.throw(_("{0} is mandatory for Return").format(self.meta.get_label("return_against")))
|
|
||||||
else:
|
|
||||||
filters = {"doctype": self.doctype, "docstatus": 1, "company": self.company}
|
|
||||||
if self.meta.get_field("customer"):
|
|
||||||
filters["customer"] = self.customer
|
|
||||||
elif self.meta.get_field("supplier"):
|
|
||||||
filters["supplier"] = self.supplier
|
|
||||||
|
|
||||||
if not frappe.db.exists(filters):
|
|
||||||
frappe.throw(_("Invalid {0}: {1}")
|
|
||||||
.format(self.meta.get_label("return_against"), self.return_against))
|
|
||||||
else:
|
|
||||||
ref_doc = frappe.get_doc(self.doctype, self.return_against)
|
|
||||||
|
|
||||||
# validate posting date time
|
|
||||||
return_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
|
||||||
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
|
||||||
|
|
||||||
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
|
||||||
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
|
||||||
|
|
||||||
# validate same exchange rate
|
|
||||||
if self.conversion_rate != ref_doc.conversion_rate:
|
|
||||||
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
|
||||||
.format(self.doctype, self.return_against, ref_doc.conversion_rate))
|
|
||||||
|
|
||||||
# validate update stock
|
|
||||||
if self.doctype == "Sales Invoice" and self.update_stock \
|
|
||||||
and not frappe.db.get_value("Sales Invoice", self.return_against, "update_stock"):
|
|
||||||
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
|
||||||
.format(self.return_against))
|
|
||||||
|
|
||||||
def validate_returned_items(self):
|
|
||||||
valid_items = frappe._dict()
|
|
||||||
for d in frappe.db.sql("""select item_code, sum(qty) as qty, rate from `tab{0} Item`
|
|
||||||
where parent = %s group by item_code""".format(self.doctype), self.return_against, as_dict=1):
|
|
||||||
valid_items.setdefault(d.item_code, d)
|
|
||||||
|
|
||||||
if self.doctype in ("Delivery Note", "Sales Invoice"):
|
|
||||||
for d in frappe.db.sql("""select item_code, sum(qty) as qty from `tabPacked Item`
|
|
||||||
where parent = %s group by item_code""".format(self.doctype), self.return_against, as_dict=1):
|
|
||||||
valid_items.setdefault(d.item_code, d)
|
|
||||||
|
|
||||||
already_returned_items = self.get_already_returned_items()
|
|
||||||
|
|
||||||
items_returned = False
|
|
||||||
for d in self.get("items"):
|
|
||||||
if flt(d.qty) < 0:
|
|
||||||
if d.item_code not in valid_items:
|
|
||||||
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
|
||||||
.format(d.idx, d.item_code, self.doctype, self.return_against))
|
|
||||||
else:
|
|
||||||
ref = valid_items.get(d.item_code, frappe._dict())
|
|
||||||
already_returned_qty = flt(already_returned_items.get(d.item_code))
|
|
||||||
max_return_qty = flt(ref.qty) - already_returned_qty
|
|
||||||
|
|
||||||
if already_returned_qty >= ref.qty:
|
|
||||||
frappe.throw(_("Item {0} has already been returned").format(d.item_code), StockOverReturnError)
|
|
||||||
elif abs(d.qty) > max_return_qty:
|
|
||||||
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
|
|
||||||
.format(d.idx, ref.qty, d.item_code), StockOverReturnError)
|
|
||||||
elif ref.rate and flt(d.rate) != ref.rate:
|
|
||||||
frappe.throw(_("Row # {0}: Rate must be same as {1} {2}")
|
|
||||||
.format(d.idx, self.doctype, self.return_against))
|
|
||||||
|
|
||||||
|
|
||||||
items_returned = True
|
|
||||||
|
|
||||||
if not items_returned:
|
|
||||||
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
|
||||||
|
|
||||||
def get_already_returned_items(self):
|
|
||||||
return frappe._dict(frappe.db.sql("""
|
|
||||||
select
|
|
||||||
child.item_code, sum(abs(child.qty)) as qty
|
|
||||||
from
|
|
||||||
`tab{0} Item` child, `tab{1}` par
|
|
||||||
where
|
|
||||||
child.parent = par.name and par.docstatus = 1
|
|
||||||
and ifnull(par.is_return, 0) = 1 and par.return_against = %s and child.qty < 0
|
|
||||||
group by item_code
|
|
||||||
""".format(self.doctype, self.doctype), self.return_against))
|
|
||||||
|
|
||||||
def calculate_taxes_and_totals(self):
|
def calculate_taxes_and_totals(self):
|
||||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||||
calculate_taxes_and_totals(self)
|
calculate_taxes_and_totals(self)
|
||||||
|
138
erpnext/controllers/sales_and_purchase_return.py
Normal file
138
erpnext/controllers/sales_and_purchase_return.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import flt, get_datetime, format_datetime
|
||||||
|
|
||||||
|
class StockOverReturnError(frappe.ValidationError): pass
|
||||||
|
|
||||||
|
|
||||||
|
def validate_return(doc):
|
||||||
|
if not doc.meta.get_field("is_return") or not doc.is_return:
|
||||||
|
return
|
||||||
|
|
||||||
|
validate_return_against(doc)
|
||||||
|
validate_returned_items(doc)
|
||||||
|
|
||||||
|
def validate_return_against(doc):
|
||||||
|
if not doc.return_against:
|
||||||
|
frappe.throw(_("{0} is mandatory for Return").format(doc.meta.get_label("return_against")))
|
||||||
|
else:
|
||||||
|
filters = {"doctype": doc.doctype, "docstatus": 1, "company": doc.company}
|
||||||
|
if doc.meta.get_field("customer"):
|
||||||
|
filters["customer"] = doc.customer
|
||||||
|
elif doc.meta.get_field("supplier"):
|
||||||
|
filters["supplier"] = doc.supplier
|
||||||
|
|
||||||
|
if not frappe.db.exists(filters):
|
||||||
|
frappe.throw(_("Invalid {0}: {1}")
|
||||||
|
.format(doc.meta.get_label("return_against"), doc.return_against))
|
||||||
|
else:
|
||||||
|
ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
|
||||||
|
|
||||||
|
# validate posting date time
|
||||||
|
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
|
||||||
|
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
||||||
|
|
||||||
|
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
||||||
|
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
||||||
|
|
||||||
|
# validate same exchange rate
|
||||||
|
if doc.conversion_rate != ref_doc.conversion_rate:
|
||||||
|
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
||||||
|
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
|
||||||
|
|
||||||
|
# validate update stock
|
||||||
|
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
|
||||||
|
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
||||||
|
.format(doc.return_against))
|
||||||
|
|
||||||
|
def validate_returned_items(doc):
|
||||||
|
valid_items = frappe._dict()
|
||||||
|
for d in frappe.db.sql("""select item_code, sum(qty) as qty, rate from `tab{0} Item`
|
||||||
|
where parent = %s group by item_code""".format(doc.doctype), doc.return_against, as_dict=1):
|
||||||
|
valid_items.setdefault(d.item_code, d)
|
||||||
|
|
||||||
|
if doc.doctype in ("Delivery Note", "Sales Invoice"):
|
||||||
|
for d in frappe.db.sql("""select item_code, sum(qty) as qty from `tabPacked Item`
|
||||||
|
where parent = %s group by item_code""".format(doc.doctype), doc.return_against, as_dict=1):
|
||||||
|
valid_items.setdefault(d.item_code, d)
|
||||||
|
|
||||||
|
already_returned_items = get_already_returned_items(doc)
|
||||||
|
|
||||||
|
items_returned = False
|
||||||
|
for d in doc.get("items"):
|
||||||
|
if flt(d.qty) < 0:
|
||||||
|
if d.item_code not in valid_items:
|
||||||
|
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
||||||
|
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
||||||
|
else:
|
||||||
|
ref = valid_items.get(d.item_code, frappe._dict())
|
||||||
|
already_returned_qty = flt(already_returned_items.get(d.item_code))
|
||||||
|
max_return_qty = flt(ref.qty) - already_returned_qty
|
||||||
|
|
||||||
|
if already_returned_qty >= ref.qty:
|
||||||
|
frappe.throw(_("Item {0} has already been returned").format(d.item_code), StockOverReturnError)
|
||||||
|
elif abs(d.qty) > max_return_qty:
|
||||||
|
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
|
||||||
|
.format(d.idx, ref.qty, d.item_code), StockOverReturnError)
|
||||||
|
elif ref.rate and flt(d.rate) != ref.rate:
|
||||||
|
frappe.throw(_("Row # {0}: Rate must be same as {1} {2}")
|
||||||
|
.format(d.idx, doc.doctype, doc.return_against))
|
||||||
|
|
||||||
|
|
||||||
|
items_returned = True
|
||||||
|
|
||||||
|
if not items_returned:
|
||||||
|
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
||||||
|
|
||||||
|
def get_already_returned_items(doc):
|
||||||
|
return frappe._dict(frappe.db.sql("""
|
||||||
|
select
|
||||||
|
child.item_code, sum(abs(child.qty)) as qty
|
||||||
|
from
|
||||||
|
`tab{0} Item` child, `tab{1}` par
|
||||||
|
where
|
||||||
|
child.parent = par.name and par.docstatus = 1
|
||||||
|
and ifnull(par.is_return, 0) = 1 and par.return_against = %s and child.qty < 0
|
||||||
|
group by item_code
|
||||||
|
""".format(doc.doctype, doc.doctype), doc.return_against))
|
||||||
|
|
||||||
|
def make_return_doc(doctype, source_name, target_doc=None):
|
||||||
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
def set_missing_values(source, target):
|
||||||
|
doc = frappe.get_doc(target)
|
||||||
|
doc.is_return = 1
|
||||||
|
doc.return_against = source.name
|
||||||
|
doc.ignore_pricing_rule = 1
|
||||||
|
doc.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
|
target_doc.qty = -1* source_doc.qty
|
||||||
|
if doctype == "Purchase Receipt":
|
||||||
|
target_doc.received_qty = -1* source_doc.qty
|
||||||
|
elif doctype == "Purchase Invoice":
|
||||||
|
target_doc.purchase_receipt = source_doc.purchase_receipt
|
||||||
|
target_doc.pr_detail = source_doc.pr_detail
|
||||||
|
|
||||||
|
doclist = get_mapped_doc(doctype, source_name, {
|
||||||
|
doctype: {
|
||||||
|
"doctype": doctype,
|
||||||
|
|
||||||
|
"validation": {
|
||||||
|
"docstatus": ["=", 1],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doctype +" Item": {
|
||||||
|
"doctype": doctype + " Item",
|
||||||
|
"fields": {
|
||||||
|
"purchase_order": "purchase_order",
|
||||||
|
"purchase_receipt": "purchase_receipt"
|
||||||
|
},
|
||||||
|
"postprocess": update_item
|
||||||
|
},
|
||||||
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
|
return doclist
|
@ -18,35 +18,31 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
|
|
||||||
// delivery note
|
// delivery note
|
||||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||||
cur_frm.add_custom_button(__('Make Delivery'), this.make_delivery_note, "icon-truck");
|
cur_frm.add_custom_button(__('Make Delivery'), this.make_delivery_note);
|
||||||
|
|
||||||
// indent
|
// indent
|
||||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||||
cur_frm.add_custom_button(__('Make ') + __('Material Request'),
|
cur_frm.add_custom_button(__('Make ') + __('Material Request'),
|
||||||
this.make_material_request, "icon-ticket");
|
this.make_material_request);
|
||||||
|
|
||||||
// sales invoice
|
// sales invoice
|
||||||
if(flt(doc.per_billed, 2) < 100) {
|
if(flt(doc.per_billed, 2) < 100) {
|
||||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice,
|
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice);
|
||||||
frappe.boot.doctype_icons["Sales Invoice"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop
|
// stop
|
||||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
||||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'],
|
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'])
|
||||||
"icon-exclamation", "btn-default")
|
|
||||||
|
|
||||||
// maintenance
|
// maintenance
|
||||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||||
cur_frm.add_custom_button(__('Make Maint. Visit'),
|
cur_frm.add_custom_button(__('Make Maint. Visit'), this.make_maintenance_visit);
|
||||||
this.make_maintenance_visit, null, "btn-default");
|
cur_frm.add_custom_button(__('Make Maint. Schedule'), this.make_maintenance_schedule);
|
||||||
cur_frm.add_custom_button(__('Make Maint. Schedule'),
|
|
||||||
this.make_maintenance_schedule, null, "btn-default");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// un-stop
|
// un-stop
|
||||||
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Sales Order'], "icon-check");
|
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Sales Order']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +60,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default");
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.order_type(doc);
|
this.order_type(doc);
|
||||||
|
@ -24,8 +24,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
|||||||
cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note);
|
cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note);
|
||||||
|
|
||||||
if (doc.docstatus==1) {
|
if (doc.docstatus==1) {
|
||||||
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return,
|
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return);
|
||||||
frappe.boot.doctype_icons["Delivery Note"]);
|
|
||||||
|
|
||||||
this.show_stock_ledger();
|
this.show_stock_ledger();
|
||||||
this.show_general_ledger();
|
this.show_general_ledger();
|
||||||
@ -33,7 +32,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
|||||||
|
|
||||||
if(doc.docstatus==0 && !doc.__islocal) {
|
if(doc.docstatus==0 && !doc.__islocal) {
|
||||||
cur_frm.add_custom_button(__('Make Packing Slip'),
|
cur_frm.add_custom_button(__('Make Packing Slip'),
|
||||||
cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"], "btn-default");
|
cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);
|
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);
|
||||||
@ -57,7 +56,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default");
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -209,7 +209,7 @@
|
|||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return",
|
"label": "Is Return",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -220,7 +220,7 @@
|
|||||||
"fieldname": "return_against",
|
"fieldname": "return_against",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Return Against Delivery Note",
|
"label": "Return Against Delivery Note",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"options": "Delivery Note",
|
"options": "Delivery Note",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
@ -1092,7 +1092,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-07-17 13:29:28.019506",
|
"modified": "2015-07-24 11:49:15.056249",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note",
|
"name": "Delivery Note",
|
||||||
|
@ -395,5 +395,5 @@ def make_packing_slip(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_return(source_name, target_doc=None):
|
def make_sales_return(source_name, target_doc=None):
|
||||||
from erpnext.utilities.transaction_base import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
return make_return_doc("Delivery Note", source_name, target_doc)
|
return make_return_doc("Delivery Note", source_name, target_doc)
|
@ -177,8 +177,9 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
self.assertRaises(SerialNoStatusError, dn.submit)
|
self.assertRaises(SerialNoStatusError, dn.submit)
|
||||||
|
|
||||||
def check_serial_no_values(self, serial_no, field_values):
|
def check_serial_no_values(self, serial_no, field_values):
|
||||||
|
serial_no = frappe.get_doc("Serial No", serial_no)
|
||||||
for field, value in field_values.items():
|
for field, value in field_values.items():
|
||||||
self.assertEquals(cstr(frappe.db.get_value("Serial No", serial_no, field)), value)
|
self.assertEquals(cstr(serial_no.get(field)), value)
|
||||||
|
|
||||||
def test_sales_return_for_non_bundled_items(self):
|
def test_sales_return_for_non_bundled_items(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
@ -287,6 +288,45 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
|
def test_return_for_serialized_items(self):
|
||||||
|
se = make_serialized_item()
|
||||||
|
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
|
||||||
|
|
||||||
|
dn = create_delivery_note(item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no)
|
||||||
|
|
||||||
|
self.check_serial_no_values(serial_no, {
|
||||||
|
"status": "Delivered",
|
||||||
|
"warehouse": "",
|
||||||
|
"delivery_document_no": dn.name
|
||||||
|
})
|
||||||
|
|
||||||
|
# return entry
|
||||||
|
dn1 = create_delivery_note(item_code="_Test Serialized Item With Series",
|
||||||
|
is_return=1, return_against=dn.name, qty=-1, rate=500, serial_no=serial_no)
|
||||||
|
|
||||||
|
self.check_serial_no_values(serial_no, {
|
||||||
|
"status": "Sales Returned",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"delivery_document_no": ""
|
||||||
|
})
|
||||||
|
|
||||||
|
dn1.cancel()
|
||||||
|
|
||||||
|
self.check_serial_no_values(serial_no, {
|
||||||
|
"status": "Delivered",
|
||||||
|
"warehouse": "",
|
||||||
|
"delivery_document_no": dn.name
|
||||||
|
})
|
||||||
|
|
||||||
|
dn.cancel()
|
||||||
|
|
||||||
|
self.check_serial_no_values(serial_no, {
|
||||||
|
"status": "Available",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"delivery_document_no": "",
|
||||||
|
"purchase_document_no": se.name
|
||||||
|
})
|
||||||
|
|
||||||
def create_delivery_note(**args):
|
def create_delivery_note(**args):
|
||||||
dn = frappe.new_doc("Delivery Note")
|
dn = frappe.new_doc("Delivery Note")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
@ -31,12 +31,10 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
|
|
||||||
if(this.frm.doc.docstatus == 1) {
|
if(this.frm.doc.docstatus == 1) {
|
||||||
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
|
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
|
||||||
cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice,
|
cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice);
|
||||||
frappe.boot.doctype_icons["Purchase Invoice"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return,
|
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return);
|
||||||
frappe.boot.doctype_icons["Purchase Receipt"]);
|
|
||||||
|
|
||||||
this.show_stock_ledger();
|
this.show_stock_ledger();
|
||||||
this.show_general_ledger();
|
this.show_general_ledger();
|
||||||
@ -54,7 +52,7 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
company: cur_frm.doc.company
|
company: cur_frm.doc.company
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, "icon-download", "btn-default");
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
|
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
|
||||||
|
@ -135,7 +135,7 @@
|
|||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return",
|
"label": "Is Return",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -146,7 +146,7 @@
|
|||||||
"fieldname": "return_against",
|
"fieldname": "return_against",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Return Against Purchase Receipt",
|
"label": "Return Against Purchase Receipt",
|
||||||
"no_copy": 1,
|
"no_copy": 0,
|
||||||
"options": "Purchase Receipt",
|
"options": "Purchase Receipt",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
@ -877,7 +877,7 @@
|
|||||||
"icon": "icon-truck",
|
"icon": "icon-truck",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-07-17 13:29:10.298448",
|
"modified": "2015-07-24 11:49:35.580382",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt",
|
"name": "Purchase Receipt",
|
||||||
|
@ -469,5 +469,5 @@ def get_invoiced_qty_map(purchase_receipt):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_return(source_name, target_doc=None):
|
def make_purchase_return(source_name, target_doc=None):
|
||||||
from erpnext.utilities.transaction_base import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
return make_return_doc("Purchase Receipt", source_name, target_doc)
|
return make_return_doc("Purchase Receipt", source_name, target_doc)
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt, cstr
|
||||||
|
|
||||||
class TestPurchaseReceipt(unittest.TestCase):
|
class TestPurchaseReceipt(unittest.TestCase):
|
||||||
def test_make_purchase_invoice(self):
|
def test_make_purchase_invoice(self):
|
||||||
@ -127,7 +127,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
|
|
||||||
return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-2)
|
return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-2)
|
||||||
|
|
||||||
|
|
||||||
# check sle
|
# check sle
|
||||||
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
||||||
"voucher_no": return_pr.name}, "outgoing_rate")
|
"voucher_no": return_pr.name}, "outgoing_rate")
|
||||||
@ -151,6 +150,34 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
|
def test_purchase_return_for_serialized_items(self):
|
||||||
|
def _check_serial_no_values(serial_no, field_values):
|
||||||
|
serial_no = frappe.get_doc("Serial No", serial_no)
|
||||||
|
for field, value in field_values.items():
|
||||||
|
self.assertEquals(cstr(serial_no.get(field)), value)
|
||||||
|
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
|
||||||
|
|
||||||
|
serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0]
|
||||||
|
|
||||||
|
_check_serial_no_values(serial_no, {
|
||||||
|
"status": "Available",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"purchase_document_no": pr.name
|
||||||
|
})
|
||||||
|
|
||||||
|
return_pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=-1,
|
||||||
|
is_return=1, return_against=pr.name, serial_no=serial_no)
|
||||||
|
|
||||||
|
_check_serial_no_values(serial_no, {
|
||||||
|
"status": "Purchase Returned",
|
||||||
|
"warehouse": "",
|
||||||
|
"purchase_document_no": pr.name,
|
||||||
|
"delivery_document_no": return_pr.name
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_gl_entries(voucher_type, voucher_no):
|
def get_gl_entries(voucher_type, voucher_no):
|
||||||
return frappe.db.sql("""select account, debit, credit
|
return frappe.db.sql("""select account, debit, credit
|
||||||
|
@ -244,7 +244,7 @@
|
|||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"label": "Delivery Document Type",
|
"label": "Delivery Document Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "\nDelivery Note\nSales Invoice\nStock Entry",
|
"options": "\nDelivery Note\nSales Invoice\nStock Entry\nPurchase Receipt",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -418,7 +418,7 @@
|
|||||||
"icon": "icon-barcode",
|
"icon": "icon-barcode",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"modified": "2015-07-13 05:28:27.961178",
|
"modified": "2015-07-24 03:55:29.946944",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Serial No",
|
"name": "Serial No",
|
||||||
|
@ -34,9 +34,6 @@ class SerialNo(StockController):
|
|||||||
self.validate_item()
|
self.validate_item()
|
||||||
self.on_stock_ledger_entry()
|
self.on_stock_ledger_entry()
|
||||||
|
|
||||||
valid_purchase_document_type = ("Purchase Receipt", "Stock Entry", "Serial No")
|
|
||||||
self.validate_value("purchase_document_type", "in", valid_purchase_document_type)
|
|
||||||
|
|
||||||
def set_maintenance_status(self):
|
def set_maintenance_status(self):
|
||||||
if not self.warranty_expiry_date and not self.amc_expiry_date:
|
if not self.warranty_expiry_date and not self.amc_expiry_date:
|
||||||
self.maintenance_status = None
|
self.maintenance_status = None
|
||||||
@ -122,6 +119,7 @@ class SerialNo(StockController):
|
|||||||
self.delivery_document_no = delivery_sle.voucher_no
|
self.delivery_document_no = delivery_sle.voucher_no
|
||||||
self.delivery_date = delivery_sle.posting_date
|
self.delivery_date = delivery_sle.posting_date
|
||||||
self.delivery_time = delivery_sle.posting_time
|
self.delivery_time = delivery_sle.posting_time
|
||||||
|
if delivery_sle.voucher_type in ("Delivery Note", "Sales Invoice"):
|
||||||
self.customer, self.customer_name = \
|
self.customer, self.customer_name = \
|
||||||
frappe.db.get_value(delivery_sle.voucher_type, delivery_sle.voucher_no,
|
frappe.db.get_value(delivery_sle.voucher_type, delivery_sle.voucher_no,
|
||||||
["customer", "customer_name"])
|
["customer", "customer_name"])
|
||||||
@ -234,7 +232,7 @@ def validate_serial_no(sle, item_det):
|
|||||||
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
|
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
|
||||||
sle.warehouse), SerialNoWarehouseError)
|
sle.warehouse), SerialNoWarehouseError)
|
||||||
|
|
||||||
if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
|
if sle.voucher_type in ("Delivery Note", "Sales Invoice") and sle.is_cancelled=="No" \
|
||||||
and sr.status != "Available":
|
and sr.status != "Available":
|
||||||
frappe.throw(_("Serial No {0} status must be 'Available' to Deliver").format(serial_no),
|
frappe.throw(_("Serial No {0} status must be 'Available' to Deliver").format(serial_no),
|
||||||
SerialNoStatusError)
|
SerialNoStatusError)
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
import frappe.share
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr, now_datetime, cint, flt
|
from frappe.utils import cstr, now_datetime, cint, flt
|
||||||
import frappe.share
|
|
||||||
|
|
||||||
from erpnext.controllers.status_updater import StatusUpdater
|
from erpnext.controllers.status_updater import StatusUpdater
|
||||||
|
|
||||||
|
class UOMMustBeIntegerError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class TransactionBase(StatusUpdater):
|
class TransactionBase(StatusUpdater):
|
||||||
def load_notification_message(self):
|
def load_notification_message(self):
|
||||||
@ -109,8 +109,6 @@ def delete_events(ref_type, ref_name):
|
|||||||
frappe.delete_doc("Event", frappe.db.sql_list("""select name from `tabEvent`
|
frappe.delete_doc("Event", frappe.db.sql_list("""select name from `tabEvent`
|
||||||
where ref_type=%s and ref_name=%s""", (ref_type, ref_name)), for_reload=True)
|
where ref_type=%s and ref_name=%s""", (ref_type, ref_name)), for_reload=True)
|
||||||
|
|
||||||
class UOMMustBeIntegerError(frappe.ValidationError): pass
|
|
||||||
|
|
||||||
def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
|
def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
|
||||||
if isinstance(qty_fields, basestring):
|
if isinstance(qty_fields, basestring):
|
||||||
qty_fields = [qty_fields]
|
qty_fields = [qty_fields]
|
||||||
@ -128,36 +126,3 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
|
|||||||
if d.get(f):
|
if d.get(f):
|
||||||
if cint(d.get(f))!=d.get(f):
|
if cint(d.get(f))!=d.get(f):
|
||||||
frappe.throw(_("Quantity cannot be a fraction in row {0}").format(d.idx), UOMMustBeIntegerError)
|
frappe.throw(_("Quantity cannot be a fraction in row {0}").format(d.idx), UOMMustBeIntegerError)
|
||||||
|
|
||||||
def make_return_doc(doctype, source_name, target_doc=None):
|
|
||||||
from frappe.model.mapper import get_mapped_doc
|
|
||||||
def set_missing_values(source, target):
|
|
||||||
doc = frappe.get_doc(target)
|
|
||||||
doc.is_return = 1
|
|
||||||
doc.return_against = source.name
|
|
||||||
doc.ignore_pricing_rule = 1
|
|
||||||
doc.run_method("calculate_taxes_and_totals")
|
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
|
||||||
target_doc.qty = -1* source_doc.qty
|
|
||||||
if doctype == "Purchase Receipt":
|
|
||||||
target_doc.received_qty = -1* source_doc.qty
|
|
||||||
elif doctype == "Purchase Invoice":
|
|
||||||
target_doc.purchase_receipt = source_doc.purchase_receipt
|
|
||||||
target_doc.pr_detail = source_doc.pr_detail
|
|
||||||
|
|
||||||
doclist = get_mapped_doc(doctype, source_name, {
|
|
||||||
doctype: {
|
|
||||||
"doctype": doctype,
|
|
||||||
|
|
||||||
"validation": {
|
|
||||||
"docstatus": ["=", 1],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doctype +" Item": {
|
|
||||||
"doctype": doctype + " Item",
|
|
||||||
"postprocess": update_item
|
|
||||||
},
|
|
||||||
}, target_doc, set_missing_values)
|
|
||||||
|
|
||||||
return doclist
|
|
||||||
|
Loading…
Reference in New Issue
Block a user