test: Add, expand and refine test-cases for zero-quantity transactions.
This commit is contained in:
parent
2815d196de
commit
b2d8a44199
@ -14,7 +14,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_ent
|
|||||||
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
|
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
|
||||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||||
from erpnext.controllers.accounts_controller import get_payment_terms
|
from erpnext.controllers.accounts_controller import InvalidQtyError, get_payment_terms
|
||||||
from erpnext.controllers.buying_controller import QtyMismatchError
|
from erpnext.controllers.buying_controller import QtyMismatchError
|
||||||
from erpnext.exceptions import InvalidCurrency
|
from erpnext.exceptions import InvalidCurrency
|
||||||
from erpnext.projects.doctype.project.test_project import make_project
|
from erpnext.projects.doctype.project.test_project import make_project
|
||||||
@ -51,6 +51,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
def test_purchase_invoice_qty(self):
|
||||||
|
pi = make_purchase_invoice(qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
pi.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
pi.items[0].qty = 1
|
||||||
|
pi.save()
|
||||||
|
self.assertEqual(pi.items[0].qty, 1)
|
||||||
|
|
||||||
def test_purchase_invoice_received_qty(self):
|
def test_purchase_invoice_received_qty(self):
|
||||||
"""
|
"""
|
||||||
1. Test if received qty is validated against accepted + rejected
|
1. Test if received qty is validated against accepted + rejected
|
||||||
@ -2094,7 +2104,7 @@ def make_purchase_invoice(**args):
|
|||||||
bundle_id = None
|
bundle_id = None
|
||||||
if args.get("batch_no") or args.get("serial_no"):
|
if args.get("batch_no") or args.get("serial_no"):
|
||||||
batches = {}
|
batches = {}
|
||||||
qty = args.qty or 5
|
qty = args.qty if args.qty is not None else 5
|
||||||
item_code = args.item or args.item_code or "_Test Item"
|
item_code = args.item or args.item_code or "_Test Item"
|
||||||
if args.get("batch_no"):
|
if args.get("batch_no"):
|
||||||
batches = frappe._dict({args.batch_no: qty})
|
batches = frappe._dict({args.batch_no: qty})
|
||||||
@ -2122,7 +2132,7 @@ def make_purchase_invoice(**args):
|
|||||||
{
|
{
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"qty": args.qty or 5,
|
"qty": args.qty if args.qty is not None else 5,
|
||||||
"received_qty": args.received_qty or 0,
|
"received_qty": args.received_qty or 0,
|
||||||
"rejected_qty": args.rejected_qty or 0,
|
"rejected_qty": args.rejected_qty or 0,
|
||||||
"rate": args.rate or 50,
|
"rate": args.rate or 50,
|
||||||
|
@ -23,7 +23,7 @@ from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_d
|
|||||||
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
|
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
|
||||||
get_depr_schedule,
|
get_depr_schedule,
|
||||||
)
|
)
|
||||||
from erpnext.controllers.accounts_controller import update_invoice_status
|
from erpnext.controllers.accounts_controller import InvalidQtyError, update_invoice_status
|
||||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
||||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||||
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
|
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
|
||||||
@ -72,6 +72,16 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
def tearDownClass(self):
|
def tearDownClass(self):
|
||||||
unlink_payment_on_cancel_of_invoice(0)
|
unlink_payment_on_cancel_of_invoice(0)
|
||||||
|
|
||||||
|
def test_sales_invoice_qty(self):
|
||||||
|
si = create_sales_invoice(qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
si.items[0].qty = 1
|
||||||
|
si.save()
|
||||||
|
self.assertEqual(si.items[0].qty, 1)
|
||||||
|
|
||||||
def test_timestamp_change(self):
|
def test_timestamp_change(self):
|
||||||
w = frappe.copy_doc(test_records[0])
|
w = frappe.copy_doc(test_records[0])
|
||||||
w.docstatus = 0
|
w.docstatus = 0
|
||||||
@ -3629,7 +3639,7 @@ def create_sales_invoice(**args):
|
|||||||
bundle_id = None
|
bundle_id = None
|
||||||
if si.update_stock and (args.get("batch_no") or args.get("serial_no")):
|
if si.update_stock and (args.get("batch_no") or args.get("serial_no")):
|
||||||
batches = {}
|
batches = {}
|
||||||
qty = args.qty or 1
|
qty = args.qty if args.qty is not None else 1
|
||||||
item_code = args.item or args.item_code or "_Test Item"
|
item_code = args.item or args.item_code or "_Test Item"
|
||||||
if args.get("batch_no"):
|
if args.get("batch_no"):
|
||||||
batches = frappe._dict({args.batch_no: qty})
|
batches = frappe._dict({args.batch_no: qty})
|
||||||
@ -3661,7 +3671,7 @@ def create_sales_invoice(**args):
|
|||||||
"description": args.description or "_Test Item",
|
"description": args.description or "_Test Item",
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"target_warehouse": args.target_warehouse,
|
"target_warehouse": args.target_warehouse,
|
||||||
"qty": args.qty or 1,
|
"qty": args.qty if args.qty is not None else 1,
|
||||||
"uom": args.uom or "Nos",
|
"uom": args.uom or "Nos",
|
||||||
"stock_uom": args.uom or "Nos",
|
"stock_uom": args.uom or "Nos",
|
||||||
"rate": args.rate if args.get("rate") is not None else 100,
|
"rate": args.rate if args.get("rate") is not None else 100,
|
||||||
|
@ -29,6 +29,8 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
|||||||
class TestPurchaseOrder(FrappeTestCase):
|
class TestPurchaseOrder(FrappeTestCase):
|
||||||
def test_purchase_order_qty(self):
|
def test_purchase_order_qty(self):
|
||||||
po = create_purchase_order(qty=1, do_not_save=True)
|
po = create_purchase_order(qty=1, do_not_save=True)
|
||||||
|
|
||||||
|
# NonNegativeError with qty=-1
|
||||||
po.append(
|
po.append(
|
||||||
"items",
|
"items",
|
||||||
{
|
{
|
||||||
@ -39,9 +41,15 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.assertRaises(frappe.NonNegativeError, po.save)
|
self.assertRaises(frappe.NonNegativeError, po.save)
|
||||||
|
|
||||||
|
# InvalidQtyError with qty=0
|
||||||
po.items[1].qty = 0
|
po.items[1].qty = 0
|
||||||
self.assertRaises(InvalidQtyError, po.save)
|
self.assertRaises(InvalidQtyError, po.save)
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
po.items[1].qty = 1
|
||||||
|
po.save()
|
||||||
|
self.assertEqual(po.items[1].qty, 1)
|
||||||
|
|
||||||
def test_make_purchase_receipt(self):
|
def test_make_purchase_receipt(self):
|
||||||
po = create_purchase_order(do_not_submit=True)
|
po = create_purchase_order(do_not_submit=True)
|
||||||
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
|
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
|
||||||
@ -1108,7 +1116,7 @@ def create_purchase_order(**args):
|
|||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"from_warehouse": args.from_warehouse,
|
"from_warehouse": args.from_warehouse,
|
||||||
"qty": args.qty or 10,
|
"qty": args.qty if args.qty is not None else 10,
|
||||||
"rate": args.rate or 500,
|
"rate": args.rate or 500,
|
||||||
"schedule_date": add_days(nowdate(), 1),
|
"schedule_date": add_days(nowdate(), 1),
|
||||||
"include_exploded_items": args.get("include_exploded_items", 1),
|
"include_exploded_items": args.get("include_exploded_items", 1),
|
||||||
|
@ -14,6 +14,7 @@ from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
|
|||||||
get_pdf,
|
get_pdf,
|
||||||
make_supplier_quotation_from_rfq,
|
make_supplier_quotation_from_rfq,
|
||||||
)
|
)
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
||||||
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
@ -21,6 +22,16 @@ from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
|||||||
|
|
||||||
|
|
||||||
class TestRequestforQuotation(FrappeTestCase):
|
class TestRequestforQuotation(FrappeTestCase):
|
||||||
|
def test_rfq_qty(self):
|
||||||
|
rfq = make_request_for_quotation(qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
rfq.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
rfq.items[0].qty = 1
|
||||||
|
rfq.save()
|
||||||
|
self.assertEqual(rfq.items[0].qty, 1)
|
||||||
|
|
||||||
def test_quote_status(self):
|
def test_quote_status(self):
|
||||||
rfq = make_request_for_quotation()
|
rfq = make_request_for_quotation()
|
||||||
|
|
||||||
@ -161,14 +172,17 @@ def make_request_for_quotation(**args) -> "RequestforQuotation":
|
|||||||
"description": "_Test Item",
|
"description": "_Test Item",
|
||||||
"uom": args.uom or "_Test UOM",
|
"uom": args.uom or "_Test UOM",
|
||||||
"stock_uom": args.stock_uom or "_Test UOM",
|
"stock_uom": args.stock_uom or "_Test UOM",
|
||||||
"qty": args.qty or 5,
|
"qty": args.qty if args.qty is not None else 5,
|
||||||
"conversion_factor": args.conversion_factor or 1.0,
|
"conversion_factor": args.conversion_factor or 1.0,
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"schedule_date": nowdate(),
|
"schedule_date": nowdate(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
rfq.submit()
|
if not args.do_not_save:
|
||||||
|
rfq.insert()
|
||||||
|
if not args.do_not_submit:
|
||||||
|
rfq.submit()
|
||||||
|
|
||||||
return rfq
|
return rfq
|
||||||
|
|
||||||
|
@ -5,8 +5,21 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
|
|
||||||
|
|
||||||
class TestPurchaseOrder(FrappeTestCase):
|
class TestPurchaseOrder(FrappeTestCase):
|
||||||
|
def test_supplier_quotation_qty(self):
|
||||||
|
sq = frappe.copy_doc(test_records[0])
|
||||||
|
sq.items[0].qty = 0
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
sq.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
sq.items[0].qty = 1
|
||||||
|
sq.save()
|
||||||
|
self.assertEqual(sq.items[0].qty, 1)
|
||||||
|
|
||||||
def test_make_purchase_order(self):
|
def test_make_purchase_order(self):
|
||||||
from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order
|
from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order
|
||||||
|
|
||||||
|
@ -42,12 +42,15 @@ def update_last_purchase_rate(doc, is_submit) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def validate_for_items(doc) -> None:
|
def validate_for_items(doc) -> None:
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
if not d.qty:
|
if not d.qty:
|
||||||
if doc.doctype == "Purchase Receipt" and d.rejected_qty:
|
if doc.doctype == "Purchase Receipt" and d.rejected_qty:
|
||||||
continue
|
continue
|
||||||
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
message = _("Please enter quantity for Item {0}").format(d.item_code)
|
||||||
|
frappe.throw(message, InvalidQtyError)
|
||||||
|
|
||||||
set_stock_levels(row=d) # update with latest quantities
|
set_stock_levels(row=d) # update with latest quantities
|
||||||
item = validate_item_and_get_basic_data(row=d)
|
item = validate_item_and_get_basic_data(row=d)
|
||||||
|
@ -7,7 +7,7 @@ from frappe import _, bold, throw
|
|||||||
from frappe.utils import cint, flt, get_link_to_form, nowtime
|
from frappe.utils import cint, flt, get_link_to_form, nowtime
|
||||||
|
|
||||||
from erpnext.accounts.party import render_address
|
from erpnext.accounts.party import render_address
|
||||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
from erpnext.controllers.accounts_controller import InvalidQtyError, get_taxes_and_charges
|
||||||
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
|
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
|
||||||
from erpnext.controllers.stock_controller import StockController
|
from erpnext.controllers.stock_controller import StockController
|
||||||
from erpnext.stock.doctype.item.item import set_item_default
|
from erpnext.stock.doctype.item.item import set_item_default
|
||||||
@ -296,7 +296,8 @@ class SellingController(StockController):
|
|||||||
il = []
|
il = []
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.qty is None:
|
if d.qty is None:
|
||||||
frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx))
|
message = _("Row {0}: Qty is mandatory").format(d.idx)
|
||||||
|
frappe.throw(message, InvalidQtyError)
|
||||||
|
|
||||||
if self.has_product_bundle(d.item_code):
|
if self.has_product_bundle(d.item_code):
|
||||||
for p in self.get("packed_items"):
|
for p in self.get("packed_items"):
|
||||||
|
@ -27,6 +27,28 @@ test_dependencies = ["Item", "Quality Inspection Template"]
|
|||||||
|
|
||||||
|
|
||||||
class TestBOM(FrappeTestCase):
|
class TestBOM(FrappeTestCase):
|
||||||
|
@timeout
|
||||||
|
def test_bom_qty(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
# No error.
|
||||||
|
bom = frappe.new_doc("BOM")
|
||||||
|
item = make_item(properties={"is_stock_item": 1})
|
||||||
|
bom.item = fg_item.item_code
|
||||||
|
bom.quantity = 1
|
||||||
|
bom.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": bom_item.item_code,
|
||||||
|
"qty": 0,
|
||||||
|
"uom": bom_item.stock_uom,
|
||||||
|
"stock_uom": bom_item.stock_uom,
|
||||||
|
"rate": 100.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
bom.save()
|
||||||
|
self.assertEqual(bom.items[0].qty, 0)
|
||||||
|
|
||||||
@timeout
|
@timeout
|
||||||
def test_get_items(self):
|
def test_get_items(self):
|
||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||||
|
@ -5,10 +5,22 @@ import frappe
|
|||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
from frappe.utils import add_days, add_months, flt, getdate, nowdate
|
from frappe.utils import add_days, add_months, flt, getdate, nowdate
|
||||||
|
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
|
|
||||||
test_dependencies = ["Product Bundle"]
|
test_dependencies = ["Product Bundle"]
|
||||||
|
|
||||||
|
|
||||||
class TestQuotation(FrappeTestCase):
|
class TestQuotation(FrappeTestCase):
|
||||||
|
def test_quotation_qty(self):
|
||||||
|
qo = make_quotation(qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
qo.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
qo.items[0].qty = 1
|
||||||
|
qo.save()
|
||||||
|
self.assertEqual(qo.items[0].qty, 1)
|
||||||
|
|
||||||
def test_make_quotation_without_terms(self):
|
def test_make_quotation_without_terms(self):
|
||||||
quotation = make_quotation(do_not_save=1)
|
quotation = make_quotation(do_not_save=1)
|
||||||
self.assertFalse(quotation.get("payment_schedule"))
|
self.assertFalse(quotation.get("payment_schedule"))
|
||||||
@ -629,7 +641,7 @@ def make_quotation(**args):
|
|||||||
{
|
{
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse,
|
"warehouse": args.warehouse,
|
||||||
"qty": args.qty or 10,
|
"qty": args.qty if args.qty is not None else 10,
|
||||||
"uom": args.uom or None,
|
"uom": args.uom or None,
|
||||||
"rate": args.rate or 100,
|
"rate": args.rate or 100,
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@ from frappe.core.doctype.user_permission.test_user_permission import create_user
|
|||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import add_days, flt, getdate, nowdate, today
|
from frappe.utils import add_days, flt, getdate, nowdate, today
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import update_child_qty_rate
|
from erpnext.controllers.accounts_controller import InvalidQtyError, update_child_qty_rate
|
||||||
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
||||||
make_maintenance_schedule,
|
make_maintenance_schedule,
|
||||||
)
|
)
|
||||||
@ -80,6 +80,29 @@ class TestSalesOrder(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
update_child_qty_rate("Sales Order", trans_item, so.name)
|
update_child_qty_rate("Sales Order", trans_item, so.name)
|
||||||
|
|
||||||
|
def test_sales_order_qty(self):
|
||||||
|
so = make_sales_order(qty=1, do_not_save=True)
|
||||||
|
|
||||||
|
# NonNegativeError with qty=-1
|
||||||
|
so.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"qty": -1,
|
||||||
|
"rate": 10,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertRaises(frappe.NonNegativeError, so.save)
|
||||||
|
|
||||||
|
# InvalidQtyError with qty=0
|
||||||
|
so.items[1].qty = 0
|
||||||
|
self.assertRaises(InvalidQtyError, so.save)
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
so.items[1].qty = 1
|
||||||
|
so.save()
|
||||||
|
self.assertEqual(so.items[0].qty, 1)
|
||||||
|
|
||||||
def test_make_material_request(self):
|
def test_make_material_request(self):
|
||||||
so = make_sales_order(do_not_submit=True)
|
so = make_sales_order(do_not_submit=True)
|
||||||
|
|
||||||
@ -2015,7 +2038,7 @@ def make_sales_order(**args):
|
|||||||
{
|
{
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse,
|
"warehouse": args.warehouse,
|
||||||
"qty": args.qty or 10,
|
"qty": args.qty if args.qty is not None else 10,
|
||||||
"uom": args.uom or None,
|
"uom": args.uom or None,
|
||||||
"price_list_rate": args.price_list_rate or None,
|
"price_list_rate": args.price_list_rate or None,
|
||||||
"discount_percentage": args.discount_percentage or None,
|
"discount_percentage": args.discount_percentage or None,
|
||||||
|
@ -10,6 +10,7 @@ from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
|
|||||||
|
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||||
automatically_fetch_payment_terms,
|
automatically_fetch_payment_terms,
|
||||||
@ -42,6 +43,16 @@ from erpnext.stock.stock_ledger import get_previous_sle
|
|||||||
|
|
||||||
|
|
||||||
class TestDeliveryNote(FrappeTestCase):
|
class TestDeliveryNote(FrappeTestCase):
|
||||||
|
def test_delivery_note_qty(self):
|
||||||
|
dn = create_delivery_note(qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
dn.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
dn.items[0].qty = 1
|
||||||
|
dn.save()
|
||||||
|
self.assertEqual(dn.items[0].qty, 1)
|
||||||
|
|
||||||
def test_over_billing_against_dn(self):
|
def test_over_billing_against_dn(self):
|
||||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
||||||
|
|
||||||
@ -1287,7 +1298,7 @@ def create_delivery_note(**args):
|
|||||||
if dn.is_return:
|
if dn.is_return:
|
||||||
type_of_transaction = "Inward"
|
type_of_transaction = "Inward"
|
||||||
|
|
||||||
qty = args.get("qty") or 1
|
qty = args.qty if args.get("qty") is not None else 1
|
||||||
qty *= -1 if type_of_transaction == "Outward" else 1
|
qty *= -1 if type_of_transaction == "Outward" else 1
|
||||||
batches = {}
|
batches = {}
|
||||||
if args.get("batch_no"):
|
if args.get("batch_no"):
|
||||||
@ -1315,7 +1326,7 @@ def create_delivery_note(**args):
|
|||||||
{
|
{
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"qty": args.qty or 1,
|
"qty": args.qty if args.get("qty") is not None else 1,
|
||||||
"rate": args.rate if args.get("rate") is not None else 100,
|
"rate": args.rate if args.get("rate") is not None else 100,
|
||||||
"conversion_factor": 1.0,
|
"conversion_factor": 1.0,
|
||||||
"serial_and_batch_bundle": bundle_id,
|
"serial_and_batch_bundle": bundle_id,
|
||||||
|
@ -9,6 +9,7 @@ import frappe
|
|||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
from frappe.utils import flt, today
|
from frappe.utils import flt, today
|
||||||
|
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.material_request.material_request import (
|
from erpnext.stock.doctype.material_request.material_request import (
|
||||||
make_in_transit_stock_entry,
|
make_in_transit_stock_entry,
|
||||||
@ -20,6 +21,17 @@ from erpnext.stock.doctype.material_request.material_request import (
|
|||||||
|
|
||||||
|
|
||||||
class TestMaterialRequest(FrappeTestCase):
|
class TestMaterialRequest(FrappeTestCase):
|
||||||
|
def test_material_request_qty(self):
|
||||||
|
mr = frappe.copy_doc(test_records[0])
|
||||||
|
mr.items[0].qty = 0
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
mr.insert()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
mr.items[0].qty = 1
|
||||||
|
mr.save()
|
||||||
|
self.assertEqual(mr.items[0].qty, 1)
|
||||||
|
|
||||||
def test_make_purchase_order(self):
|
def test_make_purchase_order(self):
|
||||||
mr = frappe.copy_doc(test_records[0]).insert()
|
mr = frappe.copy_doc(test_records[0]).insert()
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from pypika import functions as fn
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.controllers.buying_controller import QtyMismatchError
|
from erpnext.controllers.buying_controller import QtyMismatchError
|
||||||
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
|
||||||
@ -29,6 +30,23 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.set_single_value("Buying Settings", "allow_multiple_items", 1)
|
frappe.db.set_single_value("Buying Settings", "allow_multiple_items", 1)
|
||||||
|
|
||||||
|
def test_purchase_receipt_qty(self):
|
||||||
|
pr = make_purchase_receipt(qty=0, rejected_qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
pr.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
pr.items[0].qty = 1
|
||||||
|
pr.save()
|
||||||
|
self.assertEqual(pr.items[0].qty, 1)
|
||||||
|
|
||||||
|
# No error with rejected_qty=1
|
||||||
|
pr.items[0].rejected_warehouse = "_Test Rejected Warehouse - _TC"
|
||||||
|
pr.items[0].rejected_qty = 1
|
||||||
|
pr.items[0].qty = 0
|
||||||
|
pr.save()
|
||||||
|
self.assertEqual(pr.items[0].rejected_qty, 1)
|
||||||
|
|
||||||
def test_purchase_receipt_received_qty(self):
|
def test_purchase_receipt_received_qty(self):
|
||||||
"""
|
"""
|
||||||
1. Test if received qty is validated against accepted + rejected
|
1. Test if received qty is validated against accepted + rejected
|
||||||
@ -2348,7 +2366,8 @@ def make_purchase_receipt(**args):
|
|||||||
pr.is_return = args.is_return
|
pr.is_return = args.is_return
|
||||||
pr.return_against = args.return_against
|
pr.return_against = args.return_against
|
||||||
pr.apply_putaway_rule = args.apply_putaway_rule
|
pr.apply_putaway_rule = args.apply_putaway_rule
|
||||||
qty = args.qty or 5
|
|
||||||
|
qty = args.qty if args.qty is not None else 5
|
||||||
rejected_qty = args.rejected_qty or 0
|
rejected_qty = args.rejected_qty or 0
|
||||||
received_qty = args.received_qty or flt(rejected_qty) + flt(qty)
|
received_qty = args.received_qty or flt(rejected_qty) + flt(qty)
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from frappe.utils import (
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.general_ledger import process_gl_map
|
from erpnext.accounts.general_ledger import process_gl_map
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
||||||
from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, validate_bom_no
|
from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, validate_bom_no
|
||||||
from erpnext.setup.doctype.brand.brand import get_brand_defaults
|
from erpnext.setup.doctype.brand.brand import get_brand_defaults
|
||||||
@ -390,7 +391,8 @@ class StockEntry(StockController):
|
|||||||
def set_transfer_qty(self):
|
def set_transfer_qty(self):
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if not flt(item.qty):
|
if not flt(item.qty):
|
||||||
frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx), title=_("Zero quantity"))
|
message = _("Row {0}: Qty is mandatory").format(item.idx)
|
||||||
|
frappe.throw(message, InvalidQtyError, title=_("Zero quantity"))
|
||||||
if not flt(item.conversion_factor):
|
if not flt(item.conversion_factor):
|
||||||
frappe.throw(_("Row {0}: UOM Conversion Factor is mandatory").format(item.idx))
|
frappe.throw(_("Row {0}: UOM Conversion Factor is mandatory").format(item.idx))
|
||||||
item.transfer_qty = flt(
|
item.transfer_qty = flt(
|
||||||
|
@ -8,6 +8,7 @@ from frappe.tests.utils import FrappeTestCase, change_settings
|
|||||||
from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
|
from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
|
||||||
|
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.stock.doctype.item.test_item import (
|
from erpnext.stock.doctype.item.test_item import (
|
||||||
create_item,
|
create_item,
|
||||||
make_item,
|
make_item,
|
||||||
@ -54,6 +55,18 @@ class TestStockEntry(FrappeTestCase):
|
|||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def test_stock_entry_qty(self):
|
||||||
|
item_code = "_Test Item 2"
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
se = make_stock_entry(item_code=item_code, target=warehouse, qty=0, do_not_save=True)
|
||||||
|
with self.assertRaises(InvalidQtyError):
|
||||||
|
se.save()
|
||||||
|
|
||||||
|
# No error with qty=1
|
||||||
|
se.items[0].qty = 1
|
||||||
|
se.save()
|
||||||
|
self.assertEqual(se.items[0].qty, 1)
|
||||||
|
|
||||||
def test_fifo(self):
|
def test_fifo(self):
|
||||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
||||||
item_code = "_Test Item 2"
|
item_code = "_Test Item 2"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user