Merge branch 'version-12-hotfix' of https://github.com/frappe/erpnext into gstr3b_cess_v12_hotfix

This commit is contained in:
Deepesh Garg
2020-07-24 14:09:02 +05:30
26 changed files with 263 additions and 148 deletions

View File

@@ -2,6 +2,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 10",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -14,6 +15,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 12",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -26,6 +28,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 15",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -38,6 +41,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 20",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -50,6 +54,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Item Tax Template 1",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",

View File

@@ -11,6 +11,7 @@ import json
from six import string_types
import frappe
from erpnext.accounts.doctype.pricing_rule.pricing_rule import set_transaction_type
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account
@@ -418,9 +419,28 @@ def apply_pricing_rule_on_transaction(doc):
values = {}
conditions = get_other_conditions(conditions, values, doc)
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
where {conditions} and `tabPricing Rule`.disable = 0
""".format(conditions = conditions), values, as_dict=1)
args = frappe._dict({
'doctype': doc.doctype,
'transaction_type': None,
})
set_transaction_type(args)
tran_type_condition = '{} = 1'.format(args.transaction_type)
sql = """
SELECT
`tabPricing Rule`.*
FROM
`tabPricing Rule`
WHERE
{conditions} and
{tran_type_condition} and
`tabPricing Rule`.disable = 0
""".format(
conditions=conditions,
tran_type_condition=tran_type_condition,
)
pricing_rules = frappe.db.sql(sql, values, as_dict=1)
if pricing_rules:
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,

View File

@@ -1,5 +1,4 @@
{
"actions": [],
"autoname": "hash",
"creation": "2013-05-22 12:43:10",
"doctype": "DocType",
@@ -86,6 +85,7 @@
"item_tax_rate",
"bom",
"include_exploded_items",
"purchase_invoice_item",
"col_break6",
"purchase_order",
"po_detail",
@@ -764,12 +764,21 @@
"label": "Asset Category",
"options": "Asset Category",
"read_only": 1
},
{
"depends_on": "eval:parent.update_stock == 1",
"fieldname": "purchase_invoice_item",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "Purchase Invoice Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-04-07 18:34:35.104178",
"modified": "2020-06-30 16:48:01.398356",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -1,5 +1,4 @@
{
"actions": [],
"autoname": "hash",
"creation": "2013-06-04 11:02:19",
"doctype": "DocType",
@@ -87,6 +86,7 @@
"edit_references",
"sales_order",
"so_detail",
"sales_invoice_item",
"column_break_74",
"delivery_note",
"dn_detail",
@@ -783,12 +783,21 @@
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
},
{
"depends_on": "eval:parent.update_stock == 1",
"fieldname": "sales_invoice_item",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "Sales Invoice Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2019-12-04 12:22:38.517710",
"modified": "2020-06-30 16:47:37.650996",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -182,7 +182,7 @@ class BuyingController(StockController):
if item.item_code and item.qty and item.item_code in stock_and_asset_items:
item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
else flt(item.qty) / stock_and_asset_items_qty
if i == (last_item_idx - 1):
item.item_tax_amount = flt(valuation_amount_adjustment,
self.precision("item_tax_amount", item))
@@ -540,9 +540,19 @@ class BuyingController(StockController):
"serial_no": cstr(d.serial_no).strip()
})
if self.is_return:
original_incoming_rate = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": self.return_against,
"item_code": d.item_code}, "incoming_rate")
filters = {
"voucher_type": self.doctype,
"voucher_no": self.return_against,
"item_code": d.item_code
}
if (self.doctype == "Purchase Invoice" and self.update_stock
and d.get("purchase_invoice_item")):
filters["voucher_detail_no"] = d.purchase_invoice_item
elif self.doctype == "Purchase Receipt" and d.get("purchase_receipt_item"):
filters["voucher_detail_no"] = d.purchase_receipt_item
original_incoming_rate = frappe.db.get_value("Stock Ledger Entry", filters, "incoming_rate")
sle.update({
"outgoing_rate": original_incoming_rate
@@ -728,7 +738,7 @@ class BuyingController(StockController):
if delete_asset and is_auto_create_enabled:
# need to delete movements to delete assets otherwise throws link exists error
movements = frappe.db.sql(
"""SELECT asm.name
"""SELECT asm.name
FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
WHERE asm_item.parent=asm.name and asm_item.asset=%s""", asset.name, as_dict=1)
for movement in movements:

View File

@@ -278,6 +278,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.rejected_warehouse = source_doc.rejected_warehouse
target_doc.po_detail = source_doc.po_detail
target_doc.pr_detail = source_doc.pr_detail
target_doc.purchase_invoice_item = source_doc.name
elif doctype == "Delivery Note":
target_doc.against_sales_order = source_doc.against_sales_order
target_doc.against_sales_invoice = source_doc.against_sales_invoice
@@ -287,12 +289,14 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.dn_detail = source_doc.name
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
elif doctype == "Sales Invoice":
target_doc.sales_order = source_doc.sales_order
target_doc.delivery_note = source_doc.delivery_note
target_doc.so_detail = source_doc.so_detail
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
target_doc.sales_invoice_item = source_doc.name
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return

View File

@@ -216,7 +216,9 @@ class SellingController(StockController):
'target_warehouse': p.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
'allow_zero_valuation': d.allow_zero_valuation_rate,
'sales_invoice_item': d.get("sales_invoice_item"),
'delivery_note_item': d.get("dn_detail")
}))
else:
il.append(frappe._dict({
@@ -232,7 +234,9 @@ class SellingController(StockController):
'target_warehouse': d.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
'allow_zero_valuation': d.allow_zero_valuation_rate,
'sales_invoice_item': d.get("sales_invoice_item"),
'delivery_note_item': d.get("dn_detail")
}))
return il
@@ -301,7 +305,11 @@ class SellingController(StockController):
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
return_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
against_document_no = (d.get("sales_invoice_item")
if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
self.return_against, against_document_no)
# On cancellation or if return entry submission, make stock ledger entry for
# target warehouse first, to update serial no values properly

View File

@@ -297,14 +297,19 @@ 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, against_document, against_document_no=None):
incoming_rate = 0.0
cond = ''
if against_document and item_code:
if against_document_no:
cond = " and voucher_detail_no = %s" %(frappe.db.escape(against_document_no))
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s
and item_code = %s limit 1""",
and item_code = %s {0} limit 1""".format(cond),
(self.doctype, against_document, item_code))
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate

View File

@@ -13,14 +13,12 @@ class TestMapper(unittest.TestCase):
'''Test mapping of multiple source docs on a single target doc'''
make_test_records("Item")
items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0})
customers = frappe.get_all("Customer")
if items and customers:
# Make source docs (quotations) and a target doc (sales order)
customer = random.choice(customers).name
qtn1, item_list_1 = self.make_quotation(items, customer)
qtn2, item_list_2 = self.make_quotation(items, customer)
so, item_list_3 = self.make_sales_order()
items = ['_Test Item', '_Test Item 2', '_Test FG Item']
# Make source docs (quotations) and a target doc (sales order)
qtn1, item_list_1 = self.make_quotation(items, '_Test Customer')
qtn2, item_list_2 = self.make_quotation(items, '_Test Customer')
so, item_list_3 = self.make_sales_order()
# Map source docs to target with corresponding mapper method
method = "erpnext.selling.doctype.quotation.quotation.make_sales_order"
@@ -28,18 +26,12 @@ class TestMapper(unittest.TestCase):
# Assert that all inserted items are present in updated sales order
src_items = item_list_1 + item_list_2 + item_list_3
self.assertEqual(set([d.item_code for d in src_items]),
self.assertEqual(set([d for d in src_items]),
set([d.item_code for d in updated_so.items]))
def get_random_items(self, items, limit):
'''Get a number of random items from a list of given items'''
random_items = []
for i in range(0, limit):
random_items.append(random.choice(items))
return random_items
def make_quotation(self, items, customer):
item_list = self.get_random_items(items, 3)
def make_quotation(self, item_list, customer):
qtn = frappe.get_doc({
"doctype": "Quotation",
"quotation_to": "Customer",
@@ -49,7 +41,7 @@ class TestMapper(unittest.TestCase):
"valid_till" : add_months(nowdate(), 1)
})
for item in item_list:
qtn.append("items", {"qty": "2", "item_code": item.item_code})
qtn.append("items", {"qty": "2", "item_code": item})
qtn.submit()
return qtn, item_list
@@ -60,7 +52,7 @@ class TestMapper(unittest.TestCase):
"base_rate": 100.0,
"description": "CPU",
"doctype": "Sales Order Item",
"item_code": "_Test Item Home Desktop 100",
"item_code": "_Test Item",
"item_name": "CPU",
"parentfield": "items",
"qty": 10.0,
@@ -72,4 +64,4 @@ class TestMapper(unittest.TestCase):
})
so = frappe.get_doc(frappe.get_test_records('Sales Order')[0])
so.insert(ignore_permissions=True)
return so, [item]
return so, [item.item_code]

View File

@@ -30,6 +30,7 @@ class TestTaxes(unittest.TestCase):
self.item_tax_template = frappe.get_doc({
'doctype': 'Item Tax Template',
'title': uuid4(),
'company': self.company.name,
'taxes': [
{
'tax_type': self.account.name,

View File

@@ -60,12 +60,18 @@ frappe.ui.form.on("Opportunity", {
opportunity_from: function(frm) {
frm.toggle_reqd("party_name", frm.doc.opportunity_from);
frm.trigger("setup_opportunity_from");
frm.set_value("party_name","");
},
setup_opportunity_from: function(frm) {
frm.trigger('setup_queries');
frm.trigger("set_dynamic_field_label");
},
refresh: function(frm) {
var doc = frm.doc;
frm.events.opportunity_from(frm);
frm.trigger("setup_opportunity_from");
frm.trigger('toggle_mandatory');
erpnext.toggle_naming_series();

View File

@@ -151,7 +151,7 @@ def get_fee_components(fee_structure):
:param fee_structure: Fee Structure.
"""
if fee_structure:
fs = frappe.get_list("Fee Component", fields=["fees_category", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
fs = frappe.get_list("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
return fs
@@ -363,9 +363,9 @@ def get_current_enrollment(student, academic_year=None):
select
name as program_enrollment, student_name, program, student_batch_name as student_batch,
student_category, academic_term, academic_year
from
from
`tabProgram Enrollment`
where
where
student = %s and academic_year = %s
order by creation''', (student, current_academic_year), as_dict=1)

View File

@@ -161,6 +161,7 @@ frappe.ui.form.on("Fees", {
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Fee Component", "components");
row.fees_category = d.fees_category;
row.description = d.description;
row.amount = d.amount;
});
}

View File

@@ -776,10 +776,10 @@ class SalarySlip(TransactionBase):
# other taxes and charges on income tax
for d in tax_slab.other_taxes_and_charges:
if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount:
if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning:
continue
if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount:
if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning:
continue
tax_amount += tax_amount * flt(d.percent) / 100

View File

@@ -701,7 +701,7 @@
"columns": 0,
"default": "Draft",
"fieldname": "status",
"fieldtype": "Data",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1001,7 +1001,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:44.911402",
"modified": "2020-07-15 14:44:44.911402",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit",

View File

@@ -3,7 +3,7 @@ frappe.provide("erpnext.financial_statements");
erpnext.financial_statements = {
"filters": get_filters(),
"formatter": function(value, row, column, data, default_formatter) {
if (column.fieldname=="account") {
if (data && column.fieldname=="account") {
value = data.account_name || value;
column.link_onclick =
@@ -13,7 +13,7 @@ erpnext.financial_statements = {
value = default_formatter(value, row, column, data);
if (!data.parent_account) {
if (data && !data.parent_account) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");

View File

@@ -333,8 +333,8 @@ erpnext.SerialNoBatchSelector = Class.extend({
};
},
change: function () {
let val = this.get_value();
if (val.length === 0) {
const batch_no = this.get_value();
if (!batch_no) {
this.grid_row.on_grid_fields_dict
.available_qty.set_value(0);
return;
@@ -354,14 +354,11 @@ erpnext.SerialNoBatchSelector = Class.extend({
return;
}
let batch_number = me.item.batch_no ||
this.grid_row.on_grid_fields_dict.batch_no.get_value();
if (me.warehouse_details.name) {
frappe.call({
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
args: {
batch_no: batch_number,
batch_no,
warehouse: me.warehouse_details.name,
item_code: me.item_code
},

View File

@@ -7,6 +7,8 @@ from frappe import _
from frappe.utils import flt
from frappe.model.meta import get_field_precision
from frappe.utils.xlsxutils import handle_html
from six import iteritems
import json
def execute(filters=None):
return _execute(filters)
@@ -21,21 +23,24 @@ def _execute(filters=None):
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
data = []
added_item = []
for d in item_list:
row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty]
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
total_tax += flt(item_tax.get("tax_amount"))
if (d.parent, d.item_code) not in added_item:
row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty]
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
total_tax += flt(item_tax.get("tax_amount", 0))
row += [d.base_net_amount + total_tax]
row += [d.base_net_amount]
row += [d.base_net_amount + total_tax]
row += [d.base_net_amount]
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row += [item_tax.get("tax_amount", 0)]
for tax in tax_columns:
item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
row += [item_tax.get("tax_amount", 0)]
data.append(row)
data.append(row)
added_item.append((d.parent, d.item_code))
if data:
data = get_merged_data(columns, data) # merge same hsn code data
return columns, data
@@ -103,7 +108,7 @@ def get_items(filters):
match_conditions = " and {0} ".format(match_conditions)
return frappe.db.sql("""
items = frappe.db.sql("""
select
`tabSales Invoice Item`.name, `tabSales Invoice Item`.base_price_list_rate,
`tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_qty,
@@ -118,10 +123,9 @@ def get_items(filters):
""" % (conditions, match_conditions), filters, as_dict=1)
return items
def get_tax_accounts(item_list, columns, company_currency,
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
import json
def get_tax_accounts(item_list, columns, company_currency, doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
item_row_map = {}
tax_columns = []
invoice_item_row = {}
@@ -171,7 +175,7 @@ def get_tax_accounts(item_list, columns, company_currency,
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = tax_amount
if item_tax_amount:
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
itemised_tax.setdefault((parent, item_code), {})[description] = frappe._dict({
"tax_amount": flt(item_tax_amount, tax_amount_precision)
})
except ValueError:
@@ -179,42 +183,32 @@ def get_tax_accounts(item_list, columns, company_currency,
tax_columns.sort()
for desc in tax_columns:
columns.append(desc + " Amount:Currency/currency:160")
columns.append({
"label": desc,
"fieldname": frappe.scrub(desc),
"fieldtype": "Float",
"width": 110
})
# columns += ["Total Amount:Currency/currency:110"]
return itemised_tax, tax_columns
def get_merged_data(columns, data):
merged_hsn_dict = {} # to group same hsn under one key and perform row addition
add_column_index = [] # store index of columns that needs to be added
tax_col = len(get_columns())
fields_to_merge = ["stock_qty", "total_amount", "taxable_amount"] # columns for which index needs to be found
for i,d in enumerate(columns):
# check if fieldname in to_merge list and ignore tax-columns
if i < tax_col and d["fieldname"] in fields_to_merge:
add_column_index.append(i)
result = []
for row in data:
if row[0] in merged_hsn_dict:
to_add_row = merged_hsn_dict.get(row[0])
merged_hsn_dict.setdefault(row[0], {})
for i, d in enumerate(columns):
if d['fieldtype'] not in ('Int', 'Float', 'Currency'):
merged_hsn_dict[row[0]][d['fieldname']] = row[i]
else:
if merged_hsn_dict.get(row[0], {}).get(d['fieldname'], ''):
merged_hsn_dict[row[0]][d['fieldname']] += row[i]
else:
merged_hsn_dict[row[0]][d['fieldname']] = row[i]
# add columns from the add_column_index table
for k in add_column_index:
to_add_row[k] += row[k]
for key, value in iteritems(merged_hsn_dict):
result.append(value)
# add tax columns
for k in range(len(columns)):
if tax_col <= k < len(columns):
to_add_row[k] += row[k]
# update hsn dict with the newly added data
merged_hsn_dict[row[0]] = to_add_row
else:
merged_hsn_dict[row[0]] = row
# extract data rows to be displayed in report
data = [merged_hsn_dict[d] for d in merged_hsn_dict]
return data
return result

View File

@@ -1,39 +1,39 @@
[
{
"advance_paid": 0.0,
"company": "_Test Company",
"conversion_rate": 1.0,
"currency": "INR",
"customer": "_Test Customer",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer",
"doctype": "Sales Order",
"base_grand_total": 1000.0,
"grand_total": 1000.0,
"naming_series": "_T-Sales Order-",
"order_type": "Sales",
"plc_conversion_rate": 1.0,
"price_list_currency": "INR",
"company": "_Test Company",
"conversion_rate": 1.0,
"currency": "INR",
"customer": "_Test Customer",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer",
"doctype": "Sales Order",
"base_grand_total": 1000.0,
"grand_total": 1000.0,
"naming_series": "_T-Sales Order-",
"order_type": "Sales",
"plc_conversion_rate": 1.0,
"price_list_currency": "INR",
"items": [
{
"base_amount": 1000.0,
"base_rate": 100.0,
"description": "CPU",
"doctype": "Sales Order Item",
"item_code": "_Test Item Home Desktop 100",
"item_name": "CPU",
"delivery_date": "2013-02-23",
"parentfield": "items",
"qty": 10.0,
"rate": 100.0,
"base_amount": 1000.0,
"base_rate": 100.0,
"description": "CPU",
"doctype": "Sales Order Item",
"item_code": "_Test Item",
"item_name": "_Test Item 1",
"delivery_date": "2013-02-23",
"parentfield": "items",
"qty": 10.0,
"rate": 100.0,
"warehouse": "_Test Warehouse - _TC",
"stock_uom": "_Test UOM",
"conversion_factor": 1.0,
"uom": "_Test UOM"
}
],
"selling_price_list": "_Test Price List",
"territory": "_Test Territory",
],
"selling_price_list": "_Test Price List",
"territory": "_Test Territory",
"transaction_date": "2013-02-21"
}
]

View File

@@ -13,7 +13,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError,
from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
from frappe import _, msgprint
from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
now_datetime, random_string, strip)
now_datetime, random_string, strip, get_link_to_form)
from frappe.utils.html_utils import clean_html
from frappe.website.doctype.website_slideshow.website_slideshow import \
get_slideshow
@@ -634,6 +634,9 @@ class Item(WebsiteGenerator):
+ ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
def after_rename(self, old_name, new_name, merge):
if merge:
self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name)
if self.route:
invalidate_cache_for_item(self)
clear_cache(self.route)
@@ -656,6 +659,27 @@ class Item(WebsiteGenerator):
frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
json.dumps(item_wise_tax_detail), update_modified=False)
def validate_duplicate_item_in_stock_reconciliation(self, old_name, new_name):
records = frappe.db.sql(""" SELECT parent, COUNT(*) as records
FROM `tabStock Reconciliation Item`
WHERE item_code = %s and docstatus = 1
GROUP By item_code, warehouse, parent
HAVING records > 1
""", new_name, as_dict=1)
if not records: return
document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
msg = _("The items {0} and {1} are present in the following {2} : <br>"
.format(frappe.bold(old_name), frappe.bold(new_name), document))
msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
.format(frappe.bold(old_name)))
frappe.throw(_(msg), title=_("Merge not allowed"))
def set_last_purchase_rate(self, new_name):
last_purchase_rate = get_last_purchase_details(new_name).get("base_net_rate", 0)
frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)

View File

@@ -92,8 +92,7 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
"item_tax_template": "_Test Account Excise Duty @ 10",
"tax_category": ""
"item_tax_template": "_Test Account Excise Duty @ 10"
}
],
"stock_uom": "_Test UOM 1"
@@ -371,8 +370,7 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
"item_tax_template": "_Test Account Excise Duty @ 10",
"tax_category": ""
"item_tax_template": "_Test Account Excise Duty @ 10"
},
{
"doctype": "Item Tax",
@@ -451,14 +449,13 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
"item_tax_template": "_Test Account Excise Duty @ 20",
"tax_category": ""
"item_tax_template": "_Test Account Excise Duty @ 20"
},
{
"doctype": "Item Tax",
"parentfield": "taxes",
"item_tax_template": "_Test Item Tax Template 1",
"tax_category": "_Test Tax Category 1"
"tax_category": "_Test Tax Category 1",
"item_tax_template": "_Test Item Tax Template 1"
}
]
}

View File

@@ -19,6 +19,12 @@ frappe.ui.form.on('Material Request', {
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; });
frm.set_query("from_warehouse", "items", function(doc) {
return {
filters: {'company': doc.company}
};
});
},
onload: function(frm) {
@@ -27,11 +33,24 @@ frappe.ui.form.on('Material Request', {
// set schedule_date
set_schedule_date(frm);
frm.fields_dict["items"].grid.get_field("warehouse").get_query = function(doc) {
frm.set_query("warehouse", "items", function(doc) {
return {
filters: {'company': doc.company}
};
};
});
frm.set_query("set_warehouse", function(doc){
return {
filters: {'company': doc.company}
};
});
frm.set_query("set_from_warehouse", function(doc){
return {
filters: {'company': doc.company}
};
});
},
onload_post_render: function(frm) {

View File

@@ -1,6 +1,7 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:serial_no",
"creation": "2013-05-16 10:59:15",
"description": "Distinct unit of an Item",
@@ -426,7 +427,7 @@
"icon": "fa fa-barcode",
"idx": 1,
"links": [],
"modified": "2020-06-25 15:53:50.900855",
"modified": "2020-07-22 15:53:50.900855",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",

View File

@@ -193,7 +193,7 @@ class SerialNo(StockController):
def after_rename(self, old, new, merge=False):
"""rename serial_no text fields"""
for dt in frappe.db.sql("""select parent from tabDocField
where fieldname='serial_no' and fieldtype in ('Text', 'Small Text')"""):
where fieldname='serial_no' and fieldtype in ('Text', 'Small Text', 'Long Text')"""):
for item in frappe.db.sql("""select name, serial_no from `tab%s`
where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))):

View File

@@ -257,6 +257,7 @@ class StockReconciliation(StockController):
sl_entries.append(args)
qty_after_transaction = 0
for serial_no in serial_nos:
args = self.get_sle_for_items(row, [serial_no])
@@ -270,11 +271,19 @@ class StockReconciliation(StockController):
if previous_sle and row.warehouse != previous_sle.get("warehouse"):
# If serial no exists in different warehouse
warehouse = previous_sle.get("warehouse", '') or row.warehouse
if not qty_after_transaction:
qty_after_transaction = get_stock_balance(row.item_code,
warehouse, self.posting_date, self.posting_time)
qty_after_transaction -= 1
new_args = args.copy()
new_args.update({
'actual_qty': -1,
'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1,
'warehouse': previous_sle.get("warehouse", '') or row.warehouse,
'qty_after_transaction': qty_after_transaction,
'warehouse': warehouse,
'valuation_rate': previous_sle.get("valuation_rate")
})

View File

@@ -1,5 +1,4 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-03-07 18:50:32",
@@ -10,12 +9,14 @@
"field_order": [
"warehouse_detail",
"warehouse_name",
"section_break_3",
"warehouse_type",
"parent_warehouse",
"is_group",
"company",
"disabled",
"column_break_4",
"account",
"warehouse_type",
"company",
"disabled",
"address_and_contact",
"address_html",
"column_break_10",
@@ -31,7 +32,6 @@
"state",
"pin",
"tree_details",
"parent_warehouse",
"lft",
"rgt",
"old_parent"
@@ -91,6 +91,7 @@
"options": "Account"
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "address_and_contact",
"fieldtype": "Section Break",
"label": "Address and Contact"
@@ -224,13 +225,16 @@
"fieldtype": "Link",
"label": "Warehouse Type",
"options": "Warehouse Type"
},
{
"fieldname": "section_break_3",
"fieldtype": "Section Break"
}
],
"icon": "fa fa-building",
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2020-03-18 18:26:00.479541",
"modified": "2020-07-22 14:46:37.650475",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",