Merge branch 'version-12-hotfix' into batch_message

This commit is contained in:
Marica
2020-04-13 13:20:43 +05:30
committed by GitHub
16 changed files with 142 additions and 51 deletions

View File

@@ -1868,6 +1868,16 @@ class TestSalesInvoice(unittest.TestCase):
item.taxes = [] item.taxes = []
item.save() item.save()
def test_customer_provided_parts_si(self):
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
si = create_sales_invoice(item_code='CUST-0987', rate=0)
self.assertEqual(si.get("items")[0].allow_zero_valuation_rate, 1)
self.assertEqual(si.get("items")[0].amount, 0)
# test if Sales Invoice with rate is allowed
si2 = create_sales_invoice(item_code='CUST-0987', do_not_save=True)
self.assertRaises(frappe.ValidationError, si2.save)
def create_sales_invoice(**args): def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice") si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args) args = frappe._dict(args)
@@ -1890,7 +1900,7 @@ def create_sales_invoice(**args):
"gst_hsn_code": "999800", "gst_hsn_code": "999800",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1, "qty": args.qty or 1,
"rate": args.rate or 100, "rate": args.rate if args.get("rate") is not None else 100,
"income_account": args.income_account or "Sales - _TC", "income_account": args.income_account or "Sales - _TC",
"expense_account": args.expense_account or "Cost of Goods Sold - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC",

View File

@@ -82,7 +82,7 @@ class ShippingRule(Document):
if not shipping_country: if not shipping_country:
frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule')) frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule'))
if shipping_country not in [d.country for d in self.countries]: if shipping_country not in [d.country for d in self.countries]:
frappe.throw(_('Shipping rule not applicable for country {0}'.format(shipping_country))) frappe.throw(_('Shipping rule not applicable for country {0} in Shipping Address').format(shipping_country))
def add_shipping_rule_to_tax_table(self, doc, shipping_amount): def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
shipping_charge = { shipping_charge = {

View File

@@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = {
"label": __("Mode of Payment"), "label": __("Mode of Payment"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Mode of Payment" "options": "Mode of Payment"
},
{
"fieldname":"cost_center",
"label": __("Cost Center"),
"fieldtype": "Link",
"options": "Cost Center"
},
{
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
} }
] ]
} }
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt from frappe.utils import flt
from frappe import msgprint, _ from frappe import msgprint, _
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def execute(filters=None): def execute(filters=None):
return _execute(filters) return _execute(filters)
@@ -134,6 +135,38 @@ def get_conditions(filters):
if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
if filters.get("cost_center"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)"""
if filters.get("warehouse"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)"""
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
common_condition = """
and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
"""
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
else:
conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname)
return conditions return conditions
def get_invoices(filters, additional_query_columns): def get_invoices(filters, additional_query_columns):

View File

@@ -21,6 +21,7 @@ class StockController(AccountsController):
super(StockController, self).validate() super(StockController, self).validate()
self.validate_inspection() self.validate_inspection()
self.validate_serialized_batch() self.validate_serialized_batch()
self.validate_customer_provided_item()
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if self.docstatus == 2: if self.docstatus == 2:
@@ -368,6 +369,15 @@ class StockController(AccountsController):
for blanket_order in blanket_orders: for blanket_order in blanket_orders:
frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty() frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty()
def validate_customer_provided_item(self):
for d in self.get('items'):
# Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
if d.parenttype in ["Delivery Note", "Sales Invoice"] and d.rate:
frappe.throw(_("Row #{0}: {1} cannot have {2} as it is a Customer Provided Item")
.format(d.idx, frappe.bold(d.item_code), frappe.bold("Rate")))
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,
warehouse_account=None, company=None): warehouse_account=None, company=None):
def _delete_gl_entries(voucher_type, voucher_no): def _delete_gl_entries(voucher_type, voucher_no):

View File

@@ -856,7 +856,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
shipping_rule: function() { shipping_rule: function() {
var me = this; var me = this;
if(this.frm.doc.shipping_rule) { if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) {
return this.frm.call({ return this.frm.call({
doc: this.frm.doc, doc: this.frm.doc,
method: "apply_shipping_rule", method: "apply_shipping_rule",

View File

@@ -277,7 +277,7 @@ class GSTR3BReport(Document):
def get_itc_details(self, reverse_charge='N'): def get_itc_details(self, reverse_charge='N'):
itc_amount = frappe.db.sql(""" itc_amount = frappe.db.sql("""
select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
@@ -312,7 +312,7 @@ class GSTR3BReport(Document):
and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders')
group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount) as tax_amount, s.place_of_supply, s.gst_category inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category
from `tabSales Invoice` s, `tabSales Taxes and Charges` t from `tabSales Invoice` s, `tabSales Taxes and Charges` t
where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s
and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders')
@@ -385,7 +385,7 @@ class GSTR3BReport(Document):
tax_template = 'Purchase Taxes and Charges' tax_template = 'Purchase Taxes and Charges'
tax_amounts = frappe.db.sql(""" tax_amounts = frappe.db.sql("""
select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head
from `tab{doctype}` s , `tab{template}` t from `tab{doctype}` s , `tab{template}` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s

View File

@@ -111,7 +111,6 @@ class DeliveryNote(SellingController):
self.so_required() self.so_required()
self.validate_proj_cust() self.validate_proj_cust()
self.check_sales_order_on_hold_or_close("against_sales_order") self.check_sales_order_on_hold_or_close("against_sales_order")
self.validate_for_items()
self.validate_warehouse() self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "stock_qty") self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
@@ -165,12 +164,6 @@ class DeliveryNote(SellingController):
if not res: if not res:
frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
def validate_for_items(self):
for d in self.get('items'):
#Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
def validate_warehouse(self): def validate_warehouse(self):
super(DeliveryNote, self).validate_warehouse() super(DeliveryNote, self).validate_warehouse()

View File

@@ -21,6 +21,7 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
from erpnext.stock.doctype.item.test_item import create_item
class TestDeliveryNote(unittest.TestCase): class TestDeliveryNote(unittest.TestCase):
def setUp(self): def setUp(self):
@@ -433,6 +434,15 @@ class TestDeliveryNote(unittest.TestCase):
update_delivery_note_status(dn.name, "Closed") update_delivery_note_status(dn.name, "Closed")
self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed") self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed")
def test_customer_provided_parts_dn(self):
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
dn = create_delivery_note(item_code='CUST-0987', rate=0)
self.assertEqual(dn.get("items")[0].allow_zero_valuation_rate, 1)
# test if Delivery Note with rate is allowed against Customer Provided Item
dn2 = create_delivery_note(item_code='CUST-0987', do_not_save=True)
self.assertRaises(frappe.ValidationError, dn2.save)
def test_dn_billing_status_case1(self): def test_dn_billing_status_case1(self):
# SO -> DN -> SI # SO -> DN -> SI
so = make_sales_order() so = make_sales_order()
@@ -671,7 +681,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 or 1,
"rate": args.rate or 100, "rate": args.rate if args.get("rate") is not None else 100,
"conversion_factor": 1.0, "conversion_factor": 1.0,
"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1, "allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
"expense_account": args.expense_account or "Cost of Goods Sold - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC",

View File

@@ -437,6 +437,9 @@ def make_stock_entry(source_name, target_doc=None):
else: else:
target.s_warehouse = obj.warehouse target.s_warehouse = obj.warehouse
if source_parent.material_request_type == "Customer Provided":
target.allow_zero_valuation_rate = 1
def set_missing_values(source, target): def set_missing_values(source, target):
target.purpose = source.material_request_type target.purpose = source.material_request_type
if source.job_card: if source.job_card:
@@ -454,7 +457,7 @@ def make_stock_entry(source_name, target_doc=None):
"doctype": "Stock Entry", "doctype": "Stock Entry",
"validation": { "validation": {
"docstatus": ["=", 1], "docstatus": ["=", 1],
"material_request_type": ["in", ["Material Transfer", "Material Issue"]] "material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]]
} }
}, },
"Material Request Item": { "Material Request Item": {

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:serial_no", "autoname": "field:serial_no",
@@ -41,7 +42,6 @@
"delivery_document_no", "delivery_document_no",
"delivery_date", "delivery_date",
"delivery_time", "delivery_time",
"is_cancelled",
"column_break5", "column_break5",
"customer", "customer",
"customer_name", "customer_name",
@@ -56,7 +56,8 @@
"warranty_period", "warranty_period",
"more_info", "more_info",
"serial_no_details", "serial_no_details",
"company" "company",
"status"
], ],
"fields": [ "fields": [
{ {
@@ -306,16 +307,6 @@
"no_copy": 1, "no_copy": 1,
"read_only": 1 "read_only": 1
}, },
{
"fieldname": "is_cancelled",
"fieldtype": "Select",
"hidden": 1,
"label": "Is Cancelled",
"oldfieldname": "is_cancelled",
"oldfieldtype": "Select",
"options": "\nYes\nNo",
"report_hide": 1
},
{ {
"fieldname": "column_break5", "fieldname": "column_break5",
"fieldtype": "Column Break", "fieldtype": "Column Break",
@@ -423,11 +414,20 @@
"remember_last_selected_value": 1, "remember_last_selected_value": 1,
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
"options": "\nActive\nDelivered\nExpired",
"read_only": 1
} }
], ],
"icon": "fa fa-barcode", "icon": "fa fa-barcode",
"idx": 1, "idx": 1,
"modified": "2020-02-28 19:31:09.357323", "links": [],
"modified": "2020-04-08 13:29:58.517772",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Serial No", "name": "Serial No",

View File

@@ -35,6 +35,15 @@ class SerialNo(StockController):
self.set_maintenance_status() self.set_maintenance_status()
self.validate_warehouse() self.validate_warehouse()
self.validate_item() self.validate_item()
self.set_status()
def set_status(self):
if self.delivery_document_type:
self.status = "Delivered"
elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()):
self.status = "Expired"
else:
self.status = "Active"
def set_maintenance_status(self): def set_maintenance_status(self):
if not self.warranty_expiry_date and not self.amc_expiry_date: if not self.warranty_expiry_date and not self.amc_expiry_date:
@@ -197,6 +206,7 @@ class SerialNo(StockController):
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()
self.set_status()
def process_serial_no(sle): def process_serial_no(sle):
item_det = get_item_details(sle.item_code) item_det = get_item_details(sle.item_code)

View File

@@ -1,14 +1,12 @@
frappe.listview_settings['Serial No'] = { frappe.listview_settings['Serial No'] = {
add_fields: ["is_cancelled", "item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"], add_fields: ["item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"],
get_indicator: (doc) => { get_indicator: (doc) => {
if (doc.is_cancelled) { if (doc.delivery_document_type) {
return [__("Cancelled"), "red", "is_cancelled,=,Yes"]; return [__("Delivered"), "green", "delivery_document_type,is,set"];
} else if (doc.delivery_document_type) {
return [__("Delivered"), "green", "delivery_document_type,is,set|is_cancelled,=,No"];
} else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) {
return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set|is_cancelled,=,No"]; return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"];
} else { } else {
return [__("Active"), "green", "delivery_document_type,is,not set|is_cancelled,=,No"]; return [__("Active"), "green", "delivery_document_type,is,not set"];
} }
} }
}; };

View File

@@ -50,6 +50,7 @@ class StockEntry(StockController):
self.validate_posting_time() self.validate_posting_time()
self.validate_purpose() self.validate_purpose()
self.validate_item() self.validate_item()
self.validate_customer_provided_item()
self.validate_qty() self.validate_qty()
self.set_transfer_qty() self.set_transfer_qty()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
@@ -203,10 +204,6 @@ class StockEntry(StockController):
frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code), frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
frappe.MandatoryError) frappe.MandatoryError)
#Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', item.item_code, 'is_customer_provided_item'):
item.allow_zero_valuation_rate = 1
def validate_qty(self): def validate_qty(self):
manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"] manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"]

View File

@@ -744,7 +744,7 @@ class TestStockEntry(unittest.TestCase):
def test_customer_provided_parts_se(self): def test_customer_provided_parts_se(self):
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
se = make_stock_entry(item_code='CUST-0987', purporse = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC")
self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1) self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
self.assertEqual(se.get("items")[0].amount, 0) self.assertEqual(se.get("items")[0].amount, 0)