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.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):
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)
@@ -1890,7 +1900,7 @@ def create_sales_invoice(**args):
"gst_hsn_code": "999800",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"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",
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC",

View File

@@ -82,7 +82,7 @@ class ShippingRule(Document):
if not shipping_country:
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]:
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):
shipping_charge = {

View File

@@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = {
"label": __("Mode of Payment"),
"fieldtype": "Link",
"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
from frappe.utils import flt
from frappe import msgprint, _
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def execute(filters=None):
return _execute(filters)
@@ -66,7 +67,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
total_tax += tax_amount
row.append(tax_amount)
# total tax, grand total, rounded total & outstanding amount
# total tax, grand total, rounded total & outstanding amount
row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount]
data.append(row)
@@ -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("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
def get_invoices(filters, additional_query_columns):

View File

@@ -69,16 +69,16 @@ status_map = {
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
"Purchase Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Debit Note Issued",
"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
"Purchase Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Debit Note Issued",
"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
],
"Material Request": [
["Draft", None],

View File

@@ -21,6 +21,7 @@ class StockController(AccountsController):
super(StockController, self).validate()
self.validate_inspection()
self.validate_serialized_batch()
self.validate_customer_provided_item()
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if self.docstatus == 2:
@@ -368,6 +369,15 @@ class StockController(AccountsController):
for blanket_order in blanket_orders:
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,
warehouse_account=None, company=None):
def _delete_gl_entries(voucher_type, voucher_no):

View File

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

View File

@@ -277,7 +277,7 @@ class GSTR3BReport(Document):
def get_itc_details(self, reverse_charge='N'):
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
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
@@ -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')
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
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')
@@ -385,7 +385,7 @@ class GSTR3BReport(Document):
tax_template = 'Purchase Taxes and Charges'
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
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

View File

@@ -111,7 +111,6 @@ class DeliveryNote(SellingController):
self.so_required()
self.validate_proj_cust()
self.check_sales_order_on_hold_or_close("against_sales_order")
self.validate_for_items()
self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
@@ -165,12 +164,6 @@ class DeliveryNote(SellingController):
if not res:
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):
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.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.item.test_item import create_item
class TestDeliveryNote(unittest.TestCase):
def setUp(self):
@@ -433,6 +434,15 @@ class TestDeliveryNote(unittest.TestCase):
update_delivery_note_status(dn.name, "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):
# SO -> DN -> SI
so = make_sales_order()
@@ -671,7 +681,7 @@ def create_delivery_note(**args):
"item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"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,
"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
"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:
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):
target.purpose = source.material_request_type
if source.job_card:
@@ -454,7 +457,7 @@ def make_stock_entry(source_name, target_doc=None):
"doctype": "Stock Entry",
"validation": {
"docstatus": ["=", 1],
"material_request_type": ["in", ["Material Transfer", "Material Issue"]]
"material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]]
}
},
"Material Request Item": {

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:serial_no",
@@ -41,7 +42,6 @@
"delivery_document_no",
"delivery_date",
"delivery_time",
"is_cancelled",
"column_break5",
"customer",
"customer_name",
@@ -56,7 +56,8 @@
"warranty_period",
"more_info",
"serial_no_details",
"company"
"company",
"status"
],
"fields": [
{
@@ -306,16 +307,6 @@
"no_copy": 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",
"fieldtype": "Column Break",
@@ -423,11 +414,20 @@
"remember_last_selected_value": 1,
"reqd": 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",
"idx": 1,
"modified": "2020-02-28 19:31:09.357323",
"links": [],
"modified": "2020-04-08 13:29:58.517772",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",

View File

@@ -35,6 +35,15 @@ class SerialNo(StockController):
self.set_maintenance_status()
self.validate_warehouse()
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):
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_sales_details(last_sle.get("delivery_sle"))
self.set_maintenance_status()
self.set_status()
def process_serial_no(sle):
item_det = get_item_details(sle.item_code)

View File

@@ -1,14 +1,12 @@
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) => {
if (doc.is_cancelled) {
return [__("Cancelled"), "red", "is_cancelled,=,Yes"];
} else if (doc.delivery_document_type) {
return [__("Delivered"), "green", "delivery_document_type,is,set|is_cancelled,=,No"];
if (doc.delivery_document_type) {
return [__("Delivered"), "green", "delivery_document_type,is,set"];
} 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 {
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_purpose()
self.validate_item()
self.validate_customer_provided_item()
self.validate_qty()
self.set_transfer_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.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):
manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"]

View File

@@ -744,7 +744,7 @@ class TestStockEntry(unittest.TestCase):
def test_customer_provided_parts_se(self):
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].amount, 0)