Target Warehouse in Delivery Note and Sales Invoice and removed Serial No status

This commit is contained in:
Nabin Hait 2015-09-21 09:18:43 +05:30
parent adcaf75bb0
commit c865f229fb
17 changed files with 291 additions and 183 deletions

View File

@ -422,7 +422,7 @@ class SalesInvoice(SellingController):
def update_packing_list(self): def update_packing_list(self):
if cint(self.update_stock) == 1: if cint(self.update_stock) == 1:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self, 'items') make_packing_list(self)
else: else:
self.set('packed_items', []) self.set('packed_items', [])

View File

@ -688,7 +688,6 @@ class TestSalesInvoice(unittest.TestCase):
si.insert() si.insert()
si.submit() si.submit()
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Delivered")
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse")) self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"), si.name) "delivery_document_no"), si.name)
@ -702,20 +701,18 @@ class TestSalesInvoice(unittest.TestCase):
serial_nos = get_serial_nos(si.get("items")[0].serial_no) serial_nos = get_serial_nos(si.get("items")[0].serial_no)
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Available")
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC") self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no")) "delivery_document_no"))
def test_serialize_status(self): def test_serialize_status(self):
from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos, SerialNoDuplicateError from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError, get_serial_nos, SerialNoDuplicateError
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item() se = make_serialized_item()
serial_nos = get_serial_nos(se.get("items")[0].serial_no) serial_nos = get_serial_nos(se.get("items")[0].serial_no)
sr = frappe.get_doc("Serial No", serial_nos[0]) sr = frappe.get_doc("Serial No", serial_nos[0])
sr.status = "Not Available"
sr.save() sr.save()
si = frappe.copy_doc(test_records[0]) si = frappe.copy_doc(test_records[0])
@ -725,7 +722,7 @@ class TestSalesInvoice(unittest.TestCase):
si.get("items")[0].serial_no = serial_nos[0] si.get("items")[0].serial_no = serial_nos[0]
si.insert() si.insert()
self.assertRaises(SerialNoStatusError, si.submit) self.assertRaises(SerialNoWarehouseError, si.submit)
# hack! because stock ledger entires are already inserted and are not rolled back! # hack! because stock ledger entires are already inserted and are not rolled back!
self.assertRaises(SerialNoDuplicateError, si.cancel) self.assertRaises(SerialNoDuplicateError, si.cancel)

View File

@ -843,6 +843,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Target Warehouse",
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1259,7 +1282,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-20 17:18:52.752064", "modified": "2015-09-20 21:26:46.279615",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@ -173,7 +173,8 @@ class SellingController(StockController):
'uom': p.uom, 'uom': p.uom,
'batch_no': cstr(p.batch_no).strip(), 'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(), 'serial_no': cstr(p.serial_no).strip(),
'name': d.name 'name': d.name,
'target_warehouse': p.target_warehouse
})) }))
else: else:
il.append(frappe._dict({ il.append(frappe._dict({
@ -184,7 +185,8 @@ class SellingController(StockController):
'stock_uom': d.stock_uom, 'stock_uom': d.stock_uom,
'batch_no': cstr(d.get("batch_no")).strip(), 'batch_no': cstr(d.get("batch_no")).strip(),
'serial_no': cstr(d.get("serial_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(),
'name': d.name 'name': d.name,
'target_warehouse': p.target_warehouse
})) }))
return il return il

View File

@ -7,6 +7,7 @@ from frappe.utils import cint, flt, cstr
from frappe import msgprint, _ from frappe import msgprint, _
import frappe.defaults import frappe.defaults
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
@ -184,7 +185,7 @@ class StockController(AccountsController):
"voucher_no": self.name, "voucher_no": self.name,
"voucher_detail_no": d.name, "voucher_detail_no": d.name,
"actual_qty": (self.docstatus==1 and 1 or -1)*flt(d.get("stock_qty")), "actual_qty": (self.docstatus==1 and 1 or -1)*flt(d.get("stock_qty")),
"stock_uom": d.get("stock_uom"), "stock_uom": frappe.db.get_value("Item", args.get("item_code") or d.get("item_code"), "stock_uom"),
"incoming_rate": 0, "incoming_rate": 0,
"company": self.company, "company": self.company,
"fiscal_year": self.fiscal_year, "fiscal_year": self.fiscal_year,
@ -217,13 +218,14 @@ class StockController(AccountsController):
return serialized_items return serialized_items
def get_incoming_rate_for_sales_return(self, item_code, against_document): def get_incoming_rate_for_sales_return(self, item_code, warehouse, against_document):
incoming_rate = 0.0 incoming_rate = 0.0
if against_document and item_code: if against_document and item_code:
incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty) incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty)
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s and item_code = %s limit 1""", where voucher_type = %s and voucher_no = %s
(self.doctype, against_document, item_code)) and item_code = %s and warehouse=%s limit 1""",
(self.doctype, against_document, item_code, warehouse))
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0 incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate return incoming_rate
@ -252,19 +254,54 @@ class StockController(AccountsController):
sl_entries = [] sl_entries = []
for d in self.get_item_list(): for d in self.get_item_list():
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \ if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
and d.warehouse and flt(d['qty']): return_rate = 0
incoming_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1: if cint(self.is_return) and self.return_against and self.docstatus==1:
incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against) return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
d.warehouse, self.return_against)
sl_entries.append(self.get_sl_entries(d, {
"actual_qty": -1*flt(d['qty']), # On cancellation or if return entry submission, make stock ledger entry for
"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom"), # target warehouse first, to update serial no values properly
"incoming_rate": incoming_rate
}))
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) self.make_sl_entries(sl_entries)
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,

View File

@ -33,7 +33,7 @@ class SalesOrder(SellingController):
self.validate_warehouse() self.validate_warehouse()
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self,'items') make_packing_list(self)
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.set_status() self.set_status()

View File

@ -730,6 +730,29 @@
"unique": 0, "unique": 0,
"width": "150px" "width": "150px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Target Warehouse",
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1072,7 +1095,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-27 02:29:20.603580", "modified": "2015-09-20 21:26:22.732109",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@ -113,7 +113,7 @@ class DeliveryNote(SellingController):
self.validate_with_previous_doc() self.validate_with_previous_doc()
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self, 'items') make_packing_list(self)
self.update_current_stock() self.update_current_stock()

View File

@ -15,7 +15,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.doctype.stock_entry.test_stock_entry \ from erpnext.stock.doctype.stock_entry.test_stock_entry \
import make_stock_entry, make_serialized_item, get_qty_after_transaction import make_stock_entry, make_serialized_item, get_qty_after_transaction
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoStatusError from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWarehouseError
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
class TestDeliveryNote(unittest.TestCase): class TestDeliveryNote(unittest.TestCase):
@ -152,7 +152,6 @@ class TestDeliveryNote(unittest.TestCase):
dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no) dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Delivered",
"warehouse": "", "warehouse": "",
"delivery_document_no": dn.name "delivery_document_no": dn.name
}) })
@ -160,7 +159,6 @@ class TestDeliveryNote(unittest.TestCase):
dn.cancel() dn.cancel()
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Available",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"delivery_document_no": "" "delivery_document_no": ""
}) })
@ -169,12 +167,10 @@ class TestDeliveryNote(unittest.TestCase):
se = make_serialized_item() se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
frappe.db.set_value("Serial No", serial_no, "status", "Not Available")
dn = create_delivery_note(item_code="_Test Serialized Item With Series", dn = create_delivery_note(item_code="_Test Serialized Item With Series",
serial_no=serial_no, do_not_submit=True) serial_no=serial_no, do_not_submit=True)
self.assertRaises(SerialNoStatusError, dn.submit) self.assertRaises(SerialNoWarehouseError, 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) serial_no = frappe.get_doc("Serial No", serial_no)
@ -295,7 +291,6 @@ class TestDeliveryNote(unittest.TestCase):
dn = create_delivery_note(item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no) dn = create_delivery_note(item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no)
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Delivered",
"warehouse": "", "warehouse": "",
"delivery_document_no": dn.name "delivery_document_no": dn.name
}) })
@ -305,7 +300,6 @@ class TestDeliveryNote(unittest.TestCase):
is_return=1, return_against=dn.name, qty=-1, rate=500, serial_no=serial_no) is_return=1, return_against=dn.name, qty=-1, rate=500, serial_no=serial_no)
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Sales Returned",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"delivery_document_no": "" "delivery_document_no": ""
}) })
@ -313,7 +307,6 @@ class TestDeliveryNote(unittest.TestCase):
dn1.cancel() dn1.cancel()
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Delivered",
"warehouse": "", "warehouse": "",
"delivery_document_no": dn.name "delivery_document_no": dn.name
}) })
@ -321,7 +314,6 @@ class TestDeliveryNote(unittest.TestCase):
dn.cancel() dn.cancel()
self.check_serial_no_values(serial_no, { self.check_serial_no_values(serial_no, {
"status": "Available",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"delivery_document_no": "", "delivery_document_no": "",
"purchase_document_no": se.name "purchase_document_no": se.name

View File

@ -751,6 +751,29 @@
"unique": 0, "unique": 0,
"width": "100px" "width": "100px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Target Warehouse",
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1137,7 +1160,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-26 08:33:03.676574", "modified": "2015-09-20 21:26:33.043965",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note Item", "name": "Delivery Note Item",

View File

@ -64,7 +64,7 @@
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 0,
"label": "Item Name", "label": "Item Name",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "item_name", "oldfieldname": "item_name",
@ -78,29 +78,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "parent_detail_docname",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Parent Detail docname",
"no_copy": 1,
"oldfieldname": "parent_detail_docname",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -147,6 +124,27 @@
"unique": 0, "unique": 0,
"width": "300px" "width": "300px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -156,7 +154,7 @@
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"label": "Warehouse", "label": "Warehouse",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "warehouse", "oldfieldname": "warehouse",
@ -171,6 +169,50 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Target Warehouse",
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -324,20 +366,19 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "uom", "fieldname": "projected_qty",
"fieldtype": "Link", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "UOM", "label": "Projected Qty",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "uom", "oldfieldname": "projected_qty",
"oldfieldtype": "Link", "oldfieldtype": "Currency",
"options": "UOM",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@ -369,19 +410,43 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "projected_qty", "fieldname": "uom",
"fieldtype": "Float", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Projected Qty", "label": "UOM",
"no_copy": 1, "no_copy": 0,
"oldfieldname": "projected_qty", "oldfieldname": "uom",
"oldfieldtype": "Currency", "oldfieldtype": "Link",
"options": "UOM",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"fieldname": "page_break",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Page Break",
"no_copy": 0,
"oldfieldname": "page_break",
"oldfieldtype": "Check",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@ -415,21 +480,21 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "page_break", "fieldname": "parent_detail_docname",
"fieldtype": "Check", "fieldtype": "Data",
"hidden": 0, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Page Break", "label": "Parent Detail docname",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "page_break", "oldfieldname": "parent_detail_docname",
"oldfieldtype": "Check", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 1,
"read_only": 1, "read_only": 1,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -446,7 +511,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-19 12:45:53.583367", "modified": "2015-09-21 08:29:43.704286",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Packed Item", "name": "Packed Item",

View File

@ -25,67 +25,67 @@ def get_packing_item_details(item):
def get_bin_qty(item, warehouse): def get_bin_qty(item, warehouse):
det = frappe.db.sql("""select actual_qty, projected_qty from `tabBin` det = frappe.db.sql("""select actual_qty, projected_qty from `tabBin`
where item_code = %s and warehouse = %s""", (item, warehouse), as_dict = 1) where item_code = %s and warehouse = %s""", (item, warehouse), as_dict = 1)
return det and det[0] or '' return det and det[0] or frappe._dict()
def update_packing_list_item(obj, packing_item_code, qty, warehouse, line): def update_packing_list_item(doc, packing_item_code, qty, main_item_row):
bin = get_bin_qty(packing_item_code, warehouse) bin = get_bin_qty(packing_item_code, main_item_row.warehouse)
item = get_packing_item_details(packing_item_code) item = get_packing_item_details(packing_item_code)
# check if exists # check if exists
exists = 0 exists = 0
for d in obj.get("packed_items"): for d in doc.get("packed_items"):
if d.parent_item == line.item_code and d.item_code == packing_item_code and d.parent_detail_docname == line.name: if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code and d.parent_detail_docname == main_item_row.name:
pi, exists = d, 1 pi, exists = d, 1
break break
if not exists: if not exists:
pi = obj.append('packed_items', {}) pi = doc.append('packed_items', {})
pi.parent_item = line.item_code pi.parent_item = main_item_row.item_code
pi.item_code = packing_item_code pi.item_code = packing_item_code
pi.item_name = item['item_name'] pi.item_name = item.item_name
pi.parent_detail_docname = line.name pi.parent_detail_docname = main_item_row.name
pi.description = item['description'] pi.description = item.description
pi.uom = item['stock_uom'] pi.uom = item.stock_uom
pi.qty = flt(qty) pi.qty = flt(qty)
pi.actual_qty = bin and flt(bin['actual_qty']) or 0 pi.actual_qty = flt(bin.get("actual_qty"))
pi.projected_qty = bin and flt(bin['projected_qty']) or 0 pi.projected_qty = flt(bin.get("projected_qty"))
if not pi.warehouse: if not pi.warehouse:
pi.warehouse = warehouse pi.warehouse = main_item_row.warehouse
if not pi.batch_no: if not pi.batch_no:
pi.batch_no = cstr(line.get("batch_no")) pi.batch_no = cstr(main_item_row.get("batch_no"))
if not pi.target_warehouse:
pi.target_warehouse = main_item_row.get("target_warehouse")
def make_packing_list(doc):
def make_packing_list(obj, item_table_fieldname):
"""make packing list for Product Bundle item""" """make packing list for Product Bundle item"""
if obj.get("_action") and obj._action == "update_after_submit": return if doc.get("_action") and doc._action == "update_after_submit": return
parent_items = [] parent_items = []
for d in obj.get(item_table_fieldname): for d in doc.get("items"):
if frappe.db.get_value("Product Bundle", {"new_item_code": d.item_code}): if frappe.db.get_value("Product Bundle", {"new_item_code": d.item_code}):
for i in get_product_bundle_items(d.item_code): for i in get_product_bundle_items(d.item_code):
update_packing_list_item(obj, i['item_code'], flt(i['qty'])*flt(d.qty), d.warehouse, d) update_packing_list_item(doc, i.item_code, flt(i.qty)*flt(d.qty), d)
if [d.item_code, d.name] not in parent_items: if [d.item_code, d.name] not in parent_items:
parent_items.append([d.item_code, d.name]) parent_items.append([d.item_code, d.name])
cleanup_packing_list(obj, parent_items) cleanup_packing_list(doc, parent_items)
def cleanup_packing_list(obj, parent_items): def cleanup_packing_list(doc, parent_items):
"""Remove all those child items which are no longer present in main item table""" """Remove all those child items which are no longer present in main item table"""
delete_list = [] delete_list = []
for d in obj.get("packed_items"): for d in doc.get("packed_items"):
if [d.parent_item, d.parent_detail_docname] not in parent_items: if [d.parent_item, d.parent_detail_docname] not in parent_items:
# mark for deletion from doclist # mark for deletion from doclist
delete_list.append(d) delete_list.append(d)
if not delete_list: if not delete_list:
return obj return doc
packed_items = obj.get("packed_items") packed_items = doc.get("packed_items")
obj.set("packed_items", []) doc.set("packed_items", [])
for d in packed_items: for d in packed_items:
if d not in delete_list: if d not in delete_list:
obj.append("packed_items", d) doc.append("packed_items", d)

View File

@ -163,7 +163,6 @@ class TestPurchaseReceipt(unittest.TestCase):
serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0] serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0]
_check_serial_no_values(serial_no, { _check_serial_no_values(serial_no, {
"status": "Available",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"purchase_document_no": pr.name "purchase_document_no": pr.name
}) })
@ -172,7 +171,6 @@ class TestPurchaseReceipt(unittest.TestCase):
is_return=1, return_against=pr.name, serial_no=serial_no) is_return=1, return_against=pr.name, serial_no=serial_no)
_check_serial_no_values(serial_no, { _check_serial_no_values(serial_no, {
"status": "Purchase Returned",
"warehouse": "", "warehouse": "",
"purchase_document_no": pr.name, "purchase_document_no": pr.name,
"delivery_document_no": return_pr.name "delivery_document_no": return_pr.name

View File

@ -99,32 +99,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Not Available",
"description": "Only Serial Nos with status \"Available\" can be delivered.",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "Not Available\nAvailable\nDelivered\nPurchase Returned\nSales Returned",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -917,7 +891,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-20 20:39:40.751888",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Serial No", "name": "Serial No",
@ -986,7 +960,7 @@
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"search_fields": "item_code,status", "search_fields": "item_code",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC"
} }

View File

@ -16,7 +16,6 @@ class SerialNoRequiredError(ValidationError): pass
class SerialNoQtyError(ValidationError): pass class SerialNoQtyError(ValidationError): pass
class SerialNoItemError(ValidationError): pass class SerialNoItemError(ValidationError): pass
class SerialNoWarehouseError(ValidationError): pass class SerialNoWarehouseError(ValidationError): pass
class SerialNoStatusError(ValidationError): pass
class SerialNoNotExistsError(ValidationError): pass class SerialNoNotExistsError(ValidationError): pass
class SerialNoDuplicateError(ValidationError): pass class SerialNoDuplicateError(ValidationError): pass
@ -75,28 +74,6 @@ class SerialNo(StockController):
self.brand = item.brand self.brand = item.brand
self.warranty_period = item.warranty_period self.warranty_period = item.warranty_period
def set_status(self, last_sle):
if last_sle:
if last_sle.voucher_type == "Stock Entry":
document_type = frappe.db.get_value("Stock Entry", last_sle.voucher_no, "purpose")
else:
document_type = last_sle.voucher_type
if last_sle.actual_qty > 0:
if document_type in ("Delivery Note", "Sales Invoice", "Sales Return"):
self.status = "Sales Returned"
else:
self.status = "Available"
else:
if document_type in ("Purchase Receipt", "Purchase Invoice", "Purchase Return"):
self.status = "Purchase Returned"
elif document_type in ("Delivery Note", "Sales Invoice"):
self.status = "Delivered"
else:
self.status = "Not Available"
else:
self.status = "Not Available"
def set_purchase_details(self, purchase_sle): def set_purchase_details(self, purchase_sle):
if purchase_sle: if purchase_sle:
self.purchase_document_type = purchase_sle.voucher_type self.purchase_document_type = purchase_sle.voucher_type
@ -162,10 +139,10 @@ class SerialNo(StockController):
return sle_dict return sle_dict
def on_trash(self): def on_trash(self):
if self.status == 'Delivered':
frappe.throw(_("Delivered Serial No {0} cannot be deleted").format(self.name))
if self.warehouse: if self.warehouse:
frappe.throw(_("Cannot delete Serial No {0} in stock. First remove from stock, then delete.").format(self.name)) frappe.throw(_("Cannot delete Serial No {0} in stock. First remove from stock, then delete.").format(self.name))
elif self.delivery_document_no:
frappe.throw(_("Delivered Serial No {0} cannot be deleted").format(self.name))
def before_rename(self, old, new, merge=False): def before_rename(self, old, new, merge=False):
if merge: if merge:
@ -187,7 +164,6 @@ class SerialNo(StockController):
def on_stock_ledger_entry(self): def on_stock_ledger_entry(self):
if self.via_stock_ledger and not self.get("__islocal"): if self.via_stock_ledger and not self.get("__islocal"):
last_sle = self.get_last_sle() last_sle = self.get_last_sle()
self.set_status(last_sle.get("last_sle"))
self.set_purchase_details(last_sle.get("purchase_sle")) self.set_purchase_details(last_sle.get("purchase_sle"))
self.set_sales_details(last_sle.get("delivery_sle")) self.set_sales_details(last_sle.get("delivery_sle"))
self.set_maintenance_status() self.set_maintenance_status()
@ -232,10 +208,10 @@ 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") and sle.is_cancelled=="No" \ if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
and sr.status != "Available": and sle.is_cancelled=="No" and not sr.warehouse:
frappe.throw(_("Serial No {0} status must be 'Available' to Deliver").format(serial_no), frappe.throw(_("Serial No {0} does not belong to any Warehouse")
SerialNoStatusError) .format(serial_no), SerialNoWarehouseError)
elif sle.actual_qty < 0: elif sle.actual_qty < 0:
# transfer out # transfer out
@ -287,7 +263,6 @@ def make_serial_no(serial_no, sle):
sr.insert() sr.insert()
sr.warehouse = sle.warehouse sr.warehouse = sle.warehouse
sr.status = "Available"
sr.save() sr.save()
frappe.msgprint(_("Serial No {0} created").format(sr.name)) frappe.msgprint(_("Serial No {0} created").format(sr.name))
return sr.name return sr.name

View File

@ -1,12 +1,14 @@
{ {
"add_total_row": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-01-14 10:52:58", "creation": "2013-01-14 10:52:58",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"json": "{\"sort_by\": \"Serial No.name\", \"sort_order\": \"desc\", \"sort_by_next\": \"\", \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"warehouse\", \"Serial No\"], [\"status\", \"Serial No\"], [\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"], [\"purchase_document_no\", \"Serial No\"], [\"purchase_date\", \"Serial No\"], [\"customer\", \"Serial No\"], [\"customer_name\", \"Serial No\"], [\"purchase_rate\", \"Serial No\"], [\"delivery_document_no\", \"Serial No\"], [\"delivery_date\", \"Serial No\"], [\"supplier\", \"Serial No\"], [\"supplier_name\", \"Serial No\"]]}", "json": "{\"filters\":[],\"columns\":[[\"name\",\"Serial No\"],[\"item_code\",\"Serial No\"],[\"warehouse\",\"Serial No\"],[\"status\",\"Serial No\"],[\"item_name\",\"Serial No\"],[\"description\",\"Serial No\"],[\"item_group\",\"Serial No\"],[\"brand\",\"Serial No\"],[\"purchase_document_no\",\"Serial No\"],[\"purchase_date\",\"Serial No\"],[\"customer\",\"Serial No\"],[\"customer_name\",\"Serial No\"],[\"purchase_rate\",\"Serial No\"],[\"delivery_document_no\",\"Serial No\"],[\"delivery_date\",\"Serial No\"],[\"supplier\",\"Serial No\"],[\"supplier_name\",\"Serial No\"]],\"sort_by\":\"Serial No.name\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
"modified": "2014-06-03 07:18:17.327622", "modified": "2015-09-20 21:09:49.441973",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Serial No Status", "name": "Serial No Status",

View File

@ -214,8 +214,7 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin
def reset_serial_no_status_and_warehouse(serial_nos=None): def reset_serial_no_status_and_warehouse(serial_nos=None):
if not serial_nos: if not serial_nos:
serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where status != 'Not in Use' serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where docstatus = 0""")
and docstatus = 0""")
for serial_no in serial_nos: for serial_no in serial_nos:
try: try:
sr = frappe.get_doc("Serial No", serial_no) sr = frappe.get_doc("Serial No", serial_no)
@ -228,8 +227,6 @@ def reset_serial_no_status_and_warehouse(serial_nos=None):
except: except:
pass pass
frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""")
def repost_all_stock_vouchers(): def repost_all_stock_vouchers():
warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount
where ifnull(account_type, '') = 'Warehouse'""") where ifnull(account_type, '') = 'Warehouse'""")