Merge pull request #4040 from nabinhait/dn_target_warehouse
Delivery to target warehouse
This commit is contained in:
commit
c27748b2b3
@ -422,7 +422,7 @@ class SalesInvoice(SellingController):
|
||||
def update_packing_list(self):
|
||||
if cint(self.update_stock) == 1:
|
||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||
make_packing_list(self, 'items')
|
||||
make_packing_list(self)
|
||||
else:
|
||||
self.set('packed_items', [])
|
||||
|
||||
|
@ -8,6 +8,8 @@ from frappe.utils import nowdate, add_days, flt
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
|
||||
from frappe.model.naming import make_autoname
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
@ -688,7 +690,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.insert()
|
||||
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.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
|
||||
"delivery_document_no"), si.name)
|
||||
@ -702,33 +703,26 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
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.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
|
||||
"delivery_document_no"))
|
||||
|
||||
def test_serialize_status(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos, SerialNoDuplicateError
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item()
|
||||
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
||||
|
||||
sr = frappe.get_doc("Serial No", serial_nos[0])
|
||||
sr.status = "Not Available"
|
||||
sr.save()
|
||||
serial_no = frappe.get_doc({
|
||||
"doctype": "Serial No",
|
||||
"item_code": "_Test Serialized Item With Series",
|
||||
"serial_no": make_autoname("SR", "Serial No")
|
||||
})
|
||||
serial_no.save()
|
||||
|
||||
si = frappe.copy_doc(test_records[0])
|
||||
si.update_stock = 1
|
||||
si.get("items")[0].item_code = "_Test Serialized Item With Series"
|
||||
si.get("items")[0].qty = 1
|
||||
si.get("items")[0].serial_no = serial_nos[0]
|
||||
si.get("items")[0].serial_no = serial_no.name
|
||||
si.insert()
|
||||
|
||||
self.assertRaises(SerialNoStatusError, si.submit)
|
||||
|
||||
# hack! because stock ledger entires are already inserted and are not rolled back!
|
||||
self.assertRaises(SerialNoDuplicateError, si.cancel)
|
||||
self.assertRaises(SerialNoWarehouseError, si.submit)
|
||||
|
||||
def test_invoice_due_date_against_customers_credit_days(self):
|
||||
# set customer's credit days
|
||||
|
@ -843,6 +843,29 @@
|
||||
"set_only_once": 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,
|
||||
@ -1259,7 +1282,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-20 17:18:52.752064",
|
||||
"modified": "2015-09-20 21:26:46.279615",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
@ -0,0 +1,2 @@
|
||||
- Delivery to Target Warehouse: Now you can deliver items to customer's warehouse or rented warehouse. Sponsored by: **[Startrack](http://www.gps.gt/)**
|
||||
- Serial No **Status** deprecated
|
@ -173,7 +173,8 @@ class SellingController(StockController):
|
||||
'uom': p.uom,
|
||||
'batch_no': cstr(p.batch_no).strip(),
|
||||
'serial_no': cstr(p.serial_no).strip(),
|
||||
'name': d.name
|
||||
'name': d.name,
|
||||
'target_warehouse': p.target_warehouse
|
||||
}))
|
||||
else:
|
||||
il.append(frappe._dict({
|
||||
@ -184,7 +185,8 @@ class SellingController(StockController):
|
||||
'stock_uom': d.stock_uom,
|
||||
'batch_no': cstr(d.get("batch_no")).strip(),
|
||||
'serial_no': cstr(d.get("serial_no")).strip(),
|
||||
'name': d.name
|
||||
'name': d.name,
|
||||
'target_warehouse': d.target_warehouse
|
||||
}))
|
||||
return il
|
||||
|
||||
|
@ -7,6 +7,7 @@ from frappe.utils import cint, flt, cstr
|
||||
from frappe import msgprint, _
|
||||
import frappe.defaults
|
||||
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
|
||||
|
||||
@ -184,7 +185,7 @@ class StockController(AccountsController):
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": d.name,
|
||||
"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,
|
||||
"company": self.company,
|
||||
"fiscal_year": self.fiscal_year,
|
||||
@ -217,13 +218,14 @@ class StockController(AccountsController):
|
||||
|
||||
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
|
||||
if against_document and item_code:
|
||||
incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty)
|
||||
from `tabStock Ledger Entry`
|
||||
where voucher_type = %s and voucher_no = %s and item_code = %s limit 1""",
|
||||
(self.doctype, against_document, item_code))
|
||||
where voucher_type = %s and voucher_no = %s
|
||||
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
|
||||
|
||||
return incoming_rate
|
||||
@ -252,19 +254,54 @@ class StockController(AccountsController):
|
||||
|
||||
sl_entries = []
|
||||
for d in self.get_item_list():
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \
|
||||
and d.warehouse and flt(d['qty']):
|
||||
|
||||
incoming_rate = 0
|
||||
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:
|
||||
incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
||||
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"actual_qty": -1*flt(d['qty']),
|
||||
"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom"),
|
||||
"incoming_rate": incoming_rate
|
||||
}))
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
d.warehouse, 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 update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||
|
@ -33,7 +33,7 @@ class SalesOrder(SellingController):
|
||||
self.validate_warehouse()
|
||||
|
||||
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.set_status()
|
||||
|
@ -730,6 +730,29 @@
|
||||
"unique": 0,
|
||||
"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,
|
||||
"bold": 0,
|
||||
@ -1072,7 +1095,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-27 02:29:20.603580",
|
||||
"modified": "2015-09-20 21:26:22.732109",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
@ -113,7 +113,7 @@ class DeliveryNote(SellingController):
|
||||
self.validate_with_previous_doc()
|
||||
|
||||
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()
|
||||
|
||||
|
@ -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.stock_entry.test_stock_entry \
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
self.check_serial_no_values(serial_no, {
|
||||
"status": "Delivered",
|
||||
"warehouse": "",
|
||||
"delivery_document_no": dn.name
|
||||
})
|
||||
@ -160,21 +159,23 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
dn.cancel()
|
||||
|
||||
self.check_serial_no_values(serial_no, {
|
||||
"status": "Available",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"delivery_document_no": ""
|
||||
})
|
||||
|
||||
def test_serialize_status(self):
|
||||
se = make_serialized_item()
|
||||
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
|
||||
|
||||
frappe.db.set_value("Serial No", serial_no, "status", "Not Available")
|
||||
from frappe.model.naming import make_autoname
|
||||
serial_no = frappe.get_doc({
|
||||
"doctype": "Serial No",
|
||||
"item_code": "_Test Serialized Item With Series",
|
||||
"serial_no": make_autoname("SR", "Serial No")
|
||||
})
|
||||
serial_no.save()
|
||||
|
||||
dn = create_delivery_note(item_code="_Test Serialized Item With Series",
|
||||
serial_no=serial_no, do_not_submit=True)
|
||||
serial_no=serial_no.name, do_not_submit=True)
|
||||
|
||||
self.assertRaises(SerialNoStatusError, dn.submit)
|
||||
self.assertRaises(SerialNoWarehouseError, dn.submit)
|
||||
|
||||
def check_serial_no_values(self, serial_no, field_values):
|
||||
serial_no = frappe.get_doc("Serial No", serial_no)
|
||||
@ -295,7 +296,6 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
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
|
||||
})
|
||||
@ -305,7 +305,6 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
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": ""
|
||||
})
|
||||
@ -313,7 +312,6 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
dn1.cancel()
|
||||
|
||||
self.check_serial_no_values(serial_no, {
|
||||
"status": "Delivered",
|
||||
"warehouse": "",
|
||||
"delivery_document_no": dn.name
|
||||
})
|
||||
@ -321,12 +319,58 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
dn.cancel()
|
||||
|
||||
self.check_serial_no_values(serial_no, {
|
||||
"status": "Available",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"delivery_document_no": "",
|
||||
"purchase_document_no": se.name
|
||||
})
|
||||
|
||||
def test_delivery_of_bundled_items_to_target_warehouse(self):
|
||||
set_perpetual_inventory()
|
||||
|
||||
create_stock_reconciliation(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, rate=100)
|
||||
create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
|
||||
qty=50, rate=100)
|
||||
|
||||
opening_qty_test_warehouse_1 = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
|
||||
|
||||
dn = create_delivery_note(item_code="_Test Product Bundle Item",
|
||||
qty=5, rate=500, target_warehouse="_Test Warehouse 1 - _TC")
|
||||
|
||||
# qty after delivery
|
||||
actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse - _TC")
|
||||
self.assertEquals(actual_qty, 25)
|
||||
|
||||
actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
|
||||
self.assertEquals(actual_qty, opening_qty_test_warehouse_1 + 25)
|
||||
|
||||
# stock value diff for source warehouse
|
||||
stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
|
||||
"voucher_no": dn.name, "item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
||||
"stock_value_difference")
|
||||
|
||||
# stock value diff for target warehouse
|
||||
stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
|
||||
"voucher_no": dn.name, "item_code": "_Test Item", "warehouse": "_Test Warehouse 1 - _TC"},
|
||||
"stock_value_difference")
|
||||
self.assertEquals(abs(stock_value_difference), stock_value_difference1)
|
||||
|
||||
# Check gl entries
|
||||
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference)
|
||||
from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s
|
||||
and warehouse='_Test Warehouse - _TC'""", dn.name)[0][0])
|
||||
|
||||
expected_values = {
|
||||
"_Test Warehouse - _TC": [0.0, stock_value_difference],
|
||||
"_Test Warehouse 1 - _TC": [stock_value_difference, 0.0]
|
||||
}
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def create_delivery_note(**args):
|
||||
dn = frappe.new_doc("Delivery Note")
|
||||
args = frappe._dict(args)
|
||||
@ -349,7 +393,8 @@ def create_delivery_note(**args):
|
||||
"conversion_factor": 1.0,
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no
|
||||
"serial_no": args.serial_no,
|
||||
"target_warehouse": args.target_warehouse
|
||||
})
|
||||
|
||||
if not args.do_not_save:
|
||||
|
@ -751,6 +751,29 @@
|
||||
"unique": 0,
|
||||
"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,
|
||||
"bold": 0,
|
||||
@ -1137,7 +1160,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-26 08:33:03.676574",
|
||||
"modified": "2015-09-20 21:26:33.043965",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
@ -65,9 +65,8 @@ class TestLandedCostVoucher(unittest.TestCase):
|
||||
self.submit_landed_cost_voucher(pr)
|
||||
|
||||
serial_no = frappe.db.get_value("Serial No", "SN001",
|
||||
["status", "warehouse", "purchase_rate"], as_dict=1)
|
||||
["warehouse", "purchase_rate"], as_dict=1)
|
||||
|
||||
self.assertEquals(serial_no.status, "Available")
|
||||
self.assertEquals(serial_no.purchase_rate - serial_no_rate, 5.0)
|
||||
self.assertEquals(serial_no.warehouse, "_Test Warehouse - _TC")
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Item Name",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "item_name",
|
||||
@ -78,29 +78,6 @@
|
||||
"set_only_once": 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,
|
||||
"bold": 0,
|
||||
@ -147,6 +124,27 @@
|
||||
"unique": 0,
|
||||
"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,
|
||||
"bold": 0,
|
||||
@ -156,7 +154,7 @@
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Warehouse",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "warehouse",
|
||||
@ -171,6 +169,50 @@
|
||||
"set_only_once": 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,
|
||||
"bold": 0,
|
||||
@ -324,20 +366,19 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "projected_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "UOM",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "uom",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "UOM",
|
||||
"label": "Projected Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "projected_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
@ -369,19 +410,43 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "projected_qty",
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Projected Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "projected_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"label": "UOM",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "uom",
|
||||
"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,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
@ -415,21 +480,21 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "page_break",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"fieldname": "parent_detail_docname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Page Break",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "page_break",
|
||||
"oldfieldtype": "Check",
|
||||
"label": "Parent Detail docname",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "parent_detail_docname",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@ -446,7 +511,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-19 12:45:53.583367",
|
||||
"modified": "2015-09-21 08:29:43.704286",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Packed Item",
|
||||
|
@ -25,67 +25,67 @@ def get_packing_item_details(item):
|
||||
def get_bin_qty(item, warehouse):
|
||||
det = frappe.db.sql("""select actual_qty, projected_qty from `tabBin`
|
||||
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):
|
||||
bin = get_bin_qty(packing_item_code, warehouse)
|
||||
def update_packing_list_item(doc, packing_item_code, qty, main_item_row):
|
||||
bin = get_bin_qty(packing_item_code, main_item_row.warehouse)
|
||||
item = get_packing_item_details(packing_item_code)
|
||||
|
||||
# check if exists
|
||||
exists = 0
|
||||
for d in obj.get("packed_items"):
|
||||
if d.parent_item == line.item_code and d.item_code == packing_item_code and d.parent_detail_docname == line.name:
|
||||
for d in doc.get("packed_items"):
|
||||
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
|
||||
break
|
||||
|
||||
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_name = item['item_name']
|
||||
pi.parent_detail_docname = line.name
|
||||
pi.description = item['description']
|
||||
pi.uom = item['stock_uom']
|
||||
pi.item_name = item.item_name
|
||||
pi.parent_detail_docname = main_item_row.name
|
||||
pi.description = item.description
|
||||
pi.uom = item.stock_uom
|
||||
pi.qty = flt(qty)
|
||||
pi.actual_qty = bin and flt(bin['actual_qty']) or 0
|
||||
pi.projected_qty = bin and flt(bin['projected_qty']) or 0
|
||||
pi.actual_qty = flt(bin.get("actual_qty"))
|
||||
pi.projected_qty = flt(bin.get("projected_qty"))
|
||||
if not pi.warehouse:
|
||||
pi.warehouse = warehouse
|
||||
pi.warehouse = main_item_row.warehouse
|
||||
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(obj, item_table_fieldname):
|
||||
def make_packing_list(doc):
|
||||
"""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 = []
|
||||
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}):
|
||||
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:
|
||||
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"""
|
||||
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:
|
||||
# mark for deletion from doclist
|
||||
delete_list.append(d)
|
||||
|
||||
if not delete_list:
|
||||
return obj
|
||||
return doc
|
||||
|
||||
packed_items = obj.get("packed_items")
|
||||
obj.set("packed_items", [])
|
||||
packed_items = doc.get("packed_items")
|
||||
doc.set("packed_items", [])
|
||||
for d in packed_items:
|
||||
if d not in delete_list:
|
||||
obj.append("packed_items", d)
|
||||
doc.append("packed_items", d)
|
||||
|
@ -163,7 +163,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
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
|
||||
})
|
||||
@ -172,7 +171,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
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
|
||||
|
@ -17,10 +17,4 @@ cur_frm.cscript.onload = function() {
|
||||
|
||||
frappe.ui.form.on("Serial No", "refresh", function(frm) {
|
||||
frm.toggle_enable("item_code", frm.doc.__islocal);
|
||||
|
||||
if(frm.doc.status == "Sales Returned" && frm.doc.warehouse)
|
||||
cur_frm.add_custom_button(__('Set Status as Available'), function() {
|
||||
cur_frm.set_value("status", "Available");
|
||||
cur_frm.save();
|
||||
}, "icon-ok", "btn-default");
|
||||
});
|
||||
|
@ -99,32 +99,6 @@
|
||||
"set_only_once": 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,
|
||||
"bold": 0,
|
||||
@ -917,7 +891,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-09-20 20:39:40.751888",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No",
|
||||
@ -986,7 +960,7 @@
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "item_code,status",
|
||||
"search_fields": "item_code",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -16,7 +16,6 @@ class SerialNoRequiredError(ValidationError): pass
|
||||
class SerialNoQtyError(ValidationError): pass
|
||||
class SerialNoItemError(ValidationError): pass
|
||||
class SerialNoWarehouseError(ValidationError): pass
|
||||
class SerialNoStatusError(ValidationError): pass
|
||||
class SerialNoNotExistsError(ValidationError): pass
|
||||
class SerialNoDuplicateError(ValidationError): pass
|
||||
|
||||
@ -54,7 +53,7 @@ class SerialNo(StockController):
|
||||
if not self.get("__islocal"):
|
||||
item_code, warehouse = frappe.db.get_value("Serial No",
|
||||
self.name, ["item_code", "warehouse"])
|
||||
if item_code != self.item_code:
|
||||
if not self.via_stock_ledger and item_code != self.item_code:
|
||||
frappe.throw(_("Item Code cannot be changed for Serial No."),
|
||||
SerialNoCannotCannotChangeError)
|
||||
if not self.via_stock_ledger and warehouse != self.warehouse:
|
||||
@ -75,28 +74,6 @@ class SerialNo(StockController):
|
||||
self.brand = item.brand
|
||||
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):
|
||||
if purchase_sle:
|
||||
self.purchase_document_type = purchase_sle.voucher_type
|
||||
@ -162,10 +139,19 @@ class SerialNo(StockController):
|
||||
return sle_dict
|
||||
|
||||
def on_trash(self):
|
||||
if self.status == 'Delivered':
|
||||
frappe.throw(_("Delivered Serial No {0} cannot be deleted").format(self.name))
|
||||
if self.warehouse:
|
||||
frappe.throw(_("Cannot delete Serial No {0} in stock. First remove from stock, then delete.").format(self.name))
|
||||
sl_entries = frappe.db.sql("""select serial_no from `tabStock Ledger Entry`
|
||||
where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No'""",
|
||||
("%%%s%%" % self.name, self.item_code), as_dict=True)
|
||||
|
||||
# Find the exact match
|
||||
sle_exists = False
|
||||
for d in sl_entries:
|
||||
if self.name.upper() in get_serial_nos(d.serial_no):
|
||||
sle_exists = True
|
||||
break
|
||||
|
||||
if sle_exists:
|
||||
frappe.throw(_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name))
|
||||
|
||||
def before_rename(self, old, new, merge=False):
|
||||
if merge:
|
||||
@ -187,7 +173,6 @@ class SerialNo(StockController):
|
||||
def on_stock_ledger_entry(self):
|
||||
if self.via_stock_ledger and not self.get("__islocal"):
|
||||
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_sales_details(last_sle.get("delivery_sle"))
|
||||
self.set_maintenance_status()
|
||||
@ -220,9 +205,10 @@ def validate_serial_no(sle, item_det):
|
||||
sr = frappe.get_doc("Serial No", serial_no)
|
||||
|
||||
if sr.item_code!=sle.item_code:
|
||||
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
|
||||
sle.item_code), SerialNoItemError)
|
||||
|
||||
if not allow_serial_nos_with_different_item(serial_no, sle):
|
||||
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
|
||||
sle.item_code), SerialNoItemError)
|
||||
|
||||
if sr.warehouse and sle.actual_qty > 0:
|
||||
frappe.throw(_("Serial No {0} has already been received").format(sr.name),
|
||||
SerialNoDuplicateError)
|
||||
@ -232,10 +218,10 @@ def validate_serial_no(sle, item_det):
|
||||
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
|
||||
sle.warehouse), SerialNoWarehouseError)
|
||||
|
||||
if sle.voucher_type in ("Delivery Note", "Sales Invoice") and sle.is_cancelled=="No" \
|
||||
and sr.status != "Available":
|
||||
frappe.throw(_("Serial No {0} status must be 'Available' to Deliver").format(serial_no),
|
||||
SerialNoStatusError)
|
||||
if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
|
||||
and sle.is_cancelled=="No" and not sr.warehouse:
|
||||
frappe.throw(_("Serial No {0} does not belong to any Warehouse")
|
||||
.format(serial_no), SerialNoWarehouseError)
|
||||
|
||||
elif sle.actual_qty < 0:
|
||||
# transfer out
|
||||
@ -243,7 +229,24 @@ def validate_serial_no(sle, item_det):
|
||||
elif sle.actual_qty < 0 or not item_det.serial_no_series:
|
||||
frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
|
||||
SerialNoRequiredError)
|
||||
|
||||
|
||||
def allow_serial_nos_with_different_item(sle_serial_no, sle):
|
||||
"""
|
||||
Allows same serial nos for raw materials and finished goods
|
||||
in Manufacture / Repack type Stock Entry
|
||||
"""
|
||||
allow_serial_nos = False
|
||||
if sle.voucher_type=="Stock Entry" and sle.actual_qty > 0:
|
||||
stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no)
|
||||
if stock_entry.purpose in ("Repack", "Manufacture"):
|
||||
for d in stock_entry.get("items"):
|
||||
if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse):
|
||||
serial_nos = get_serial_nos(d.serial_no)
|
||||
if sle_serial_no in serial_nos:
|
||||
allow_serial_nos = True
|
||||
|
||||
return allow_serial_nos
|
||||
|
||||
def update_serial_nos(sle, item_det):
|
||||
if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 \
|
||||
and item_det.has_serial_no == 1 and item_det.serial_no_series:
|
||||
@ -260,6 +263,7 @@ def update_serial_nos(sle, item_det):
|
||||
if frappe.db.exists("Serial No", serial_no):
|
||||
sr = frappe.get_doc("Serial No", serial_no)
|
||||
sr.via_stock_ledger = True
|
||||
sr.item_code = sle.item_code
|
||||
sr.warehouse = sle.warehouse if sle.actual_qty > 0 else None
|
||||
sr.save(ignore_permissions=True)
|
||||
elif sle.actual_qty > 0:
|
||||
@ -287,7 +291,6 @@ def make_serial_no(serial_no, sle):
|
||||
sr.insert()
|
||||
|
||||
sr.warehouse = sle.warehouse
|
||||
sr.status = "Available"
|
||||
sr.save()
|
||||
frappe.msgprint(_("Serial No {0} created").format(sr.name))
|
||||
return sr.name
|
||||
|
@ -359,14 +359,17 @@ class StockEntry(StockController):
|
||||
|
||||
def update_stock_ledger(self):
|
||||
sl_entries = []
|
||||
|
||||
# make sl entries for source warehouse first, then do for target warehouse
|
||||
for d in self.get('items'):
|
||||
if cstr(d.s_warehouse) and self.docstatus == 1:
|
||||
if cstr(d.s_warehouse):
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"warehouse": cstr(d.s_warehouse),
|
||||
"actual_qty": -flt(d.transfer_qty),
|
||||
"incoming_rate": 0
|
||||
}))
|
||||
|
||||
|
||||
for d in self.get('items'):
|
||||
if cstr(d.t_warehouse):
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"warehouse": cstr(d.t_warehouse),
|
||||
@ -374,15 +377,18 @@ class StockEntry(StockController):
|
||||
"incoming_rate": flt(d.valuation_rate)
|
||||
}))
|
||||
|
||||
# On cancellation, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
# On cancellation, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
|
||||
if cstr(d.s_warehouse) and self.docstatus == 2:
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"warehouse": cstr(d.s_warehouse),
|
||||
"actual_qty": -flt(d.transfer_qty),
|
||||
"incoming_rate": 0
|
||||
}))
|
||||
# if cstr(d.s_warehouse) and self.docstatus == 2:
|
||||
# sl_entries.append(self.get_sl_entries(d, {
|
||||
# "warehouse": cstr(d.s_warehouse),
|
||||
# "actual_qty": -flt(d.transfer_qty),
|
||||
# "incoming_rate": 0
|
||||
# }))
|
||||
|
||||
if self.docstatus == 2:
|
||||
sl_entries.reverse()
|
||||
|
||||
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
|
||||
|
||||
|
@ -566,6 +566,27 @@ class TestStockEntry(unittest.TestCase):
|
||||
stock_entry = frappe.get_doc(make_stock_entry(production_order.name, "Manufacture", 1))
|
||||
stock_entry.insert()
|
||||
self.assertTrue("_Test Variant Item-S" in [d.item_code for d in stock_entry.items])
|
||||
|
||||
def test_same_serial_nos_in_repack_or_manufacture_entries(self):
|
||||
s1 = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
|
||||
serial_nos = s1.get("items")[0].serial_no
|
||||
|
||||
s2 = make_stock_entry(item_code="_Test Serialized Item With Series", source="_Test Warehouse - _TC",
|
||||
qty=2, basic_rate=100, purpose="Repack", serial_no=serial_nos, do_not_save=True)
|
||||
|
||||
s2.append("items", {
|
||||
"item_code": "_Test Serialized Item",
|
||||
"t_warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 2,
|
||||
"basic_rate": 120,
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"serial_no": serial_nos
|
||||
})
|
||||
|
||||
s2.submit()
|
||||
s2.cancel()
|
||||
|
||||
def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
|
||||
se = frappe.copy_doc(test_records[0])
|
||||
@ -616,7 +637,8 @@ def make_stock_entry(**args):
|
||||
"basic_rate": args.basic_rate,
|
||||
"expense_account": args.expense_account or "Stock Adjustment - _TC",
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no
|
||||
})
|
||||
|
||||
if not args.do_not_save:
|
||||
|
@ -1,12 +1,14 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-01-14 10:52:58",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"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\"]]}",
|
||||
"modified": "2014-06-03 07:18:17.327622",
|
||||
"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": "2015-09-20 21:09:49.441973",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No Status",
|
||||
|
@ -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):
|
||||
if not serial_nos:
|
||||
serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where status != 'Not in Use'
|
||||
and docstatus = 0""")
|
||||
serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where docstatus = 0""")
|
||||
for serial_no in serial_nos:
|
||||
try:
|
||||
sr = frappe.get_doc("Serial No", serial_no)
|
||||
@ -228,8 +227,6 @@ def reset_serial_no_status_and_warehouse(serial_nos=None):
|
||||
except:
|
||||
pass
|
||||
|
||||
frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""")
|
||||
|
||||
def repost_all_stock_vouchers():
|
||||
warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount
|
||||
where ifnull(account_type, '') = 'Warehouse'""")
|
||||
|
@ -45,5 +45,13 @@
|
||||
For Mandrill Integration <a href="https://github.com/frappe/erpnext/issues/3546">#3546</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 30%">
|
||||
<a href="http://www.gps.gt">Startrack</a>
|
||||
</td>
|
||||
<td>
|
||||
For Delivery to Target Warehouse <a href="https://github.com/frappe/erpnext/issues/3970">#3546</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
Loading…
x
Reference in New Issue
Block a user