Merge branch 'develop' into project-naming-series-patch

This commit is contained in:
Marica 2021-01-28 13:01:28 +05:30 committed by GitHub
commit 465974cff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 366 additions and 245 deletions

View File

@ -21,6 +21,7 @@
"book_asset_depreciation_entry_automatically",
"add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms",
"delete_linked_ledger_entries",
"deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on",
@ -219,6 +220,12 @@
"fieldtype": "Select",
"label": "Book Deferred Entries Based On",
"options": "Days\nMonths"
},
{
"default": "0",
"fieldname": "delete_linked_ledger_entries",
"fieldtype": "Check",
"label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction"
}
],
"icon": "icon-cog",
@ -226,7 +233,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2020-10-13 11:32:52.268826",
"modified": "2021-01-05 13:04:00.118892",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@ -254,4 +261,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
}
}

View File

@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(doc):
validate_disabled(doc)
# Validate with existing taxes and charges template for unique tax category
validate_for_tax_category(doc)
for tax in doc.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, doc)
@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc):
def validate_disabled(doc):
if doc.is_default and doc.disabled:
frappe.throw(_("Disabled template must not be default template"))
def validate_for_tax_category(doc):
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))

View File

@ -152,7 +152,7 @@
<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
</tr>

View File

@ -381,7 +381,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
per_ordered: ["<", 100],
company: me.frm.doc.company
}
})

View File

@ -224,7 +224,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
per_ordered: ["<", 100],
company: me.frm.doc.company
}
})
@ -280,7 +280,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99]
per_ordered: ["<", 100]
}
});
dialog.hide();

View File

@ -44,7 +44,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
per_ordered: ["<", 100],
company: me.frm.doc.company
}
})

View File

@ -118,6 +118,12 @@ class AccountsController(TransactionBase):
def before_cancel(self):
validate_einvoice_fields(self)
def on_trash(self):
# delete sl and gl entries on deletion of transaction
if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'):
frappe.db.sql("delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
frappe.db.sql("delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
def validate_deferred_start_and_end_date(self):
for d in self.items:

View File

@ -416,9 +416,6 @@ regional_overrides = {
'Italy': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
},
'Germany': {
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional',
}
}
user_privacy_documents = [

View File

@ -88,7 +88,7 @@ def get_events(start, end, filters=None):
def add_assignments(events, start, end, conditions=None):
query = """select name, start_date, end_date, employee_name,
employee, docstatus
employee, docstatus, shift_type
from `tabShift Assignment` where
start_date >= %(start_date)s
or end_date <= %(end_date)s
@ -97,18 +97,40 @@ def add_assignments(events, start, end, conditions=None):
if conditions:
query += conditions
for d in frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True):
e = {
"name": d.name,
"doctype": "Shift Assignment",
"start_date": d.start_date,
"end_date": d.end_date if d.end_date else nowdate(),
"title": cstr(d.employee_name) + ": "+ \
cstr(d.shift_type),
"docstatus": d.docstatus
}
if e not in events:
events.append(e)
records = frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True)
shift_timing_map = get_shift_type_timing([d.shift_type for d in records])
for d in records:
daily_event_start = d.start_date
daily_event_end = d.end_date if d.end_date else getdate()
delta = timedelta(days=1)
while daily_event_start <= daily_event_end:
start_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['start_time']
end_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['end_time']
daily_event_start += delta
e = {
"name": d.name,
"doctype": "Shift Assignment",
"start_date": start_timing,
"end_date": end_timing,
"title": cstr(d.employee_name) + ": "+ \
cstr(d.shift_type),
"docstatus": d.docstatus,
"allDay": 0
}
if e not in events:
events.append(e)
return events
def get_shift_type_timing(shift_types):
shift_timing_map = {}
data = frappe.get_all("Shift Type", filters = {"name": ("IN", shift_types)}, fields = ['name', 'start_time', 'end_time'])
for d in data:
shift_timing_map[d.name] = d
return shift_timing_map
def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):

View File

@ -6,14 +6,8 @@ frappe.views.calendar["Shift Assignment"] = {
"start": "start_date",
"end": "end_date",
"id": "name",
"docstatus": 1
},
options: {
header: {
left: 'prev,next today',
center: 'title',
right: 'month'
}
"docstatus": 1,
"allDay": "allDay",
},
get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
}

View File

@ -43,22 +43,24 @@ def get_data(filters):
currency = erpnext.get_company_currency(filters.get('company'))
for key, qty in iteritems(pledge_values):
row = {}
current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
valid_upto = loan_security_details.get(key[1], {}).get('valid_upto')
if qty:
row = {}
current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
valid_upto = loan_security_details.get(key[1], {}).get('valid_upto')
row.update(loan_security_details.get(key[1]))
row.update({
'applicant_type': applicant_type_map.get(key[0]),
'applicant_name': key[0],
'total_qty': qty,
'current_value': current_value,
'price_valid_upto': valid_upto,
'portfolio_percent': flt(current_value * 100 / total_value_map.get(key[0]), 2),
'currency': currency
})
row.update(loan_security_details.get(key[1]))
row.update({
'applicant_type': applicant_type_map.get(key[0]),
'applicant_name': key[0],
'total_qty': qty,
'current_value': current_value,
'price_valid_upto': valid_upto,
'portfolio_percent': flt(current_value * 100 / total_value_map.get(key[0]), 2) if total_value_map.get(key[0]) \
else 0.0,
'currency': currency
})
data.append(row)
data.append(row)
return data

View File

@ -40,21 +40,22 @@ def get_data(filters):
currency = erpnext.get_company_currency(filters.get('company'))
for security, value in iteritems(current_pledges):
row = {}
current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0))
valid_upto = loan_security_details.get(security, {}).get('valid_upto')
if value.get('qty'):
row = {}
current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0))
valid_upto = loan_security_details.get(security, {}).get('valid_upto')
row.update(loan_security_details.get(security))
row.update({
'total_qty': value.get('qty'),
'current_value': current_value,
'price_valid_upto': valid_upto,
'portfolio_percent': flt(current_value * 100 / total_portfolio_value, 2),
'pledged_applicant_count': value.get('applicant_count'),
'currency': currency
})
row.update(loan_security_details.get(security))
row.update({
'total_qty': value.get('qty'),
'current_value': current_value,
'price_valid_upto': valid_upto,
'portfolio_percent': flt(current_value * 100 / total_portfolio_value, 2),
'pledged_applicant_count': value.get('applicant_count'),
'currency': currency
})
data.append(row)
data.append(row)
return data

View File

@ -53,8 +53,7 @@ frappe.ui.form.on('Additional Salary', {
if (!frm.doc.company) return;
frm.set_query("salary_component", function() {
return {
query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
filters: {type: "earning", company: frm.doc.company}
filters: {type: ["in", ["earning", "deduction"]], company: frm.doc.company}
};
});
},

View File

@ -10,15 +10,7 @@ frappe.ui.form.on('Employee Incentive', {
}
};
});
if (!frm.doc.company) return;
frm.set_query("salary_component", function() {
return {
query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
filters: {type: "earning", company: frm.doc.company}
};
});
frm.trigger('set_earning_component');
},
employee: function(frm) {
@ -45,11 +37,21 @@ frappe.ui.form.on('Employee Incentive', {
callback: function(data) {
if (data.message) {
frm.set_value("company", data.message.company);
frm.trigger('set_earning_component');
}
}
});
},
set_earning_component: function(frm) {
if (!frm.doc.company) return;
frm.set_query("salary_component", function() {
return {
filters: {type: "earning", company: frm.doc.company}
};
});
},
get_employee_currency: function(frm) {
frappe.call({
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",

View File

@ -58,13 +58,11 @@ frappe.ui.form.on('Salary Structure', {
if(!frm.doc.company) return;
frm.set_query("salary_component", "earnings", function() {
return {
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
filters: {type: "earning", company: frm.doc.company}
};
});
frm.set_query("salary_component", "deductions", function() {
return {
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
filters: {type: "deduction", company: frm.doc.company}
};
});

View File

@ -207,22 +207,3 @@ def get_employees(salary_structure):
return list(set([d.employee for d in employees]))
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters):
if len(filters) < 2:
return {}
return frappe.db.sql("""
select t1.salary_component
from `tabSalary Component` t1, `tabSalary Component Account` t2
where (t1.name = t2.parent
and t1.type = %(type)s
and t2.company = %(company)s)
or (t1.type = %(type)s
and t1.statistical_component = 1)
order by salary_component
""",{
"type": filters['type'],
"company": filters['company']
})

View File

@ -195,6 +195,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
this._super(doc, cdt, cdn);
},
batch_no: function(doc, cdt, cdn) {
this._super(doc, cdt, cdn);
},
received_qty: function(doc, cdt, cdn) {
this.calculate_accepted_qty(doc, cdt, cdn)
},

View File

@ -589,11 +589,21 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
.then((r) => {
if (r.message &&
(r.message.has_batch_no || r.message.has_serial_no)) {
(r.message.has_batch_no || r.message.has_serial_no)) {
frappe.flags.hide_serial_batch_dialog = false;
}
});
},
() => {
// check if batch serial selector is disabled or not
if (show_batch_dialog && !frappe.flags.hide_serial_batch_dialog)
return frappe.db.get_single_value('Stock Settings', 'disable_serial_no_and_batch_selector')
.then((value) => {
if (value) {
frappe.flags.hide_serial_batch_dialog = true;
}
});
},
() => {
if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) {
var d = locals[cdt][cdn];
@ -1099,6 +1109,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
},
batch_no: function(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
this.apply_price_list(item, true);
},
toggle_conversion_factor: function(item) {
// toggle read only property for conversion factor field if the uom and stock uom are same
if(this.frm.get_field('items').grid.fields_map.conversion_factor) {
@ -1403,6 +1418,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"pricing_rules": d.pricing_rules,
"warehouse": d.warehouse,
"serial_no": d.serial_no,
"batch_no": d.batch_no,
"price_list_rate": d.price_list_rate,
"conversion_factor": d.conversion_factor || 1.0
});

View File

@ -7,13 +7,14 @@
"engine": "InnoDB",
"field_order": [
"client",
"account_number_length",
"column_break_2",
"client_number",
"section_break_4",
"column_break_2",
"consultant_number",
"consultant",
"section_break_4",
"account_number_length",
"column_break_6",
"consultant_number"
"temporary_against_account_number"
],
"fields": [
{
@ -66,10 +67,17 @@
"fieldtype": "Int",
"label": "Account Number Length",
"reqd": 1
},
{
"allow_in_quick_entry": 1,
"fieldname": "temporary_against_account_number",
"fieldtype": "Data",
"label": "Temporary Against Account Number",
"reqd": 1
}
],
"links": [],
"modified": "2020-11-05 17:52:11.674329",
"modified": "2020-11-19 19:00:09.088816",
"modified_by": "Administrator",
"module": "Regional",
"name": "DATEV Settings",

View File

@ -1,53 +0,0 @@
import frappe
from frappe import _
from frappe import msgprint
REQUIRED_FIELDS = {
"Sales Invoice": [
{
"field_name": "company_address",
"regulation": "§ 14 Abs. 4 Nr. 1 UStG"
},
{
"field_name": "company_tax_id",
"regulation": "§ 14 Abs. 4 Nr. 2 UStG"
},
{
"field_name": "taxes",
"regulation": "§ 14 Abs. 4 Nr. 8 UStG"
},
{
"field_name": "customer_address",
"regulation": "§ 14 Abs. 4 Nr. 1 UStG",
"condition": "base_grand_total > 250"
}
]
}
def validate_regional(doc):
"""Check if required fields for this document are present."""
required_fields = REQUIRED_FIELDS.get(doc.doctype)
if not required_fields:
return
meta = frappe.get_meta(doc.doctype)
field_map = {field.fieldname: field.label for field in meta.fields}
for field in required_fields:
condition = field.get("condition")
if condition and not frappe.safe_eval(condition, doc.as_dict()):
continue
field_name = field.get("field_name")
regulation = field.get("regulation")
if field_name and not doc.get(field_name):
missing(field_map.get(field_name), regulation)
def missing(field_label, regulation):
"""Notify the user that a required field is missing."""
translated_msg = _('Remember to set {field_label}. It is required by {regulation}.', context='Specific for Germany. Example: Remember to set Company Tax ID. It is required by § 14 Abs. 4 Nr. 2 UStG.') # noqa: E501
formatted_msg = translated_msg.format(field_label=frappe.bold(_(field_label)), regulation=regulation)
msgprint(formatted_msg)

View File

@ -1,12 +0,0 @@
import frappe
import unittest
from erpnext.regional.germany.accounts_controller import validate_regional
class TestAccountsController(unittest.TestCase):
def setUp(self):
self.sales_invoice = frappe.get_last_doc('Sales Invoice')
def test_validate_regional(self):
validate_regional(self.sales_invoice)

View File

@ -96,6 +96,8 @@ def execute(filters=None):
"""Entry point for frappe."""
data = []
if filters and validate(filters):
fn = 'temporary_against_account_number'
filters[fn] = frappe.get_value('DATEV Settings', filters.get('company'), fn)
data = get_transactions(filters, as_dict=0)
return COLUMNS, data
@ -156,11 +158,11 @@ def get_transactions(filters, as_dict=1):
case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
/* account number or, if empty, party account number */
coalesce(acc.account_number, acc_pa.account_number) as 'Konto',
acc.account_number as 'Konto',
/* against number or, if empty, party against number */
coalesce(acc_against.account_number, acc_against_pa.account_number) as 'Gegenkonto (ohne BU-Schlüssel)',
%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
gl.posting_date as 'Belegdatum',
gl.voucher_no as 'Belegfeld 1',
LEFT(gl.remarks, 60) as 'Buchungstext',
@ -171,27 +173,10 @@ def get_transactions(filters, as_dict=1):
FROM `tabGL Entry` gl
/* Statistisches Konto (Debitoren/Kreditoren) */
left join `tabParty Account` pa
on gl.against = pa.parent
and gl.company = pa.company
/* Kontonummer */
left join `tabAccount` acc
on gl.account = acc.name
/* Gegenkonto-Nummer */
left join `tabAccount` acc_against
on gl.against = acc_against.name
/* Statistische Kontonummer */
left join `tabAccount` acc_pa
on pa.account = acc_pa.name
/* Statistische Gegenkonto-Nummer */
left join `tabAccount` acc_against_pa
on pa.account = acc_against_pa.name
WHERE gl.company = %(company)s
AND DATE(gl.posting_date) >= %(from_date)s
AND DATE(gl.posting_date) <= %(to_date)s
@ -347,7 +332,9 @@ def download_datev_csv(filters):
coa = frappe.get_value('Company', company, 'chart_of_accounts')
filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length')
datev_settings = frappe.get_doc('DATEV Settings', company)
filters['account_number_length'] = datev_settings.account_number_length
filters['temporary_against_account_number'] = datev_settings.temporary_against_account_number
transactions = get_transactions(filters)
account_names = get_account_names(filters)

View File

@ -126,7 +126,8 @@ def make_datev_settings(company):
"doctype": "DATEV Settings",
"client": company.name,
"client_number": "12345",
"consultant_number": "67890"
"consultant_number": "67890",
"temporary_against_account_number": "9999"
}).insert()
@ -137,7 +138,8 @@ class TestDatev(TestCase):
self.filters = {
"company": self.company.name,
"from_date": today(),
"to_date": today()
"to_date": today(),
"temporary_against_account_number": "9999"
}
make_datev_settings(self.company)

View File

@ -255,15 +255,16 @@ class Gstr1Report(object):
for item_code, tax_amounts in item_wise_tax_detail.items():
tax_rate = tax_amounts[0]
if cgst_or_sgst:
tax_rate *= 2
if parent not in self.cgst_sgst_invoices:
self.cgst_sgst_invoices.append(parent)
if tax_rate:
if cgst_or_sgst:
tax_rate *= 2
if parent not in self.cgst_sgst_invoices:
self.cgst_sgst_invoices.append(parent)
rate_based_dict = self.items_based_on_tax_rate\
.setdefault(parent, {}).setdefault(tax_rate, [])
if item_code not in rate_based_dict:
rate_based_dict.append(item_code)
rate_based_dict = self.items_based_on_tax_rate\
.setdefault(parent, {}).setdefault(tax_rate, [])
if item_code not in rate_based_dict:
rate_based_dict.append(item_code)
except ValueError:
continue
if unidentified_gst_accounts:

View File

@ -399,6 +399,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
}
},
batch_no: function(doc, cdt, cdn) {
this._super(doc, cdt, cdn);
},
qty: function(doc, cdt, cdn) {
this._super(doc, cdt, cdn);

View File

@ -8,6 +8,8 @@ import unittest
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
from frappe.utils import cint, flt
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.stock.get_item_details import get_item_details
class TestBatch(unittest.TestCase):
def test_item_has_batch_enabled(self):
@ -182,7 +184,7 @@ class TestBatch(unittest.TestCase):
stock_entry.cancel()
current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
self.assertEqual(current_batch_qty, existing_batch_qty)
@classmethod
def make_new_batch_and_entry(cls, item_name, batch_name, warehouse):
'''Make a new stock entry for given target warehouse and batch name of item'''
@ -252,6 +254,72 @@ class TestBatch(unittest.TestCase):
return batch
def test_batch_wise_item_price(self):
if not frappe.db.get_value('Item', '_Test Batch Price Item'):
frappe.get_doc({
'doctype': 'Item',
'is_stock_item': 1,
'item_code': '_Test Batch Price Item',
'item_group': 'Products',
'has_batch_no': 1,
'create_new_batch': 1
}).insert(ignore_permissions=True)
batch1 = create_batch('_Test Batch Price Item', 200, 1)
batch2 = create_batch('_Test Batch Price Item', 300, 1)
batch3 = create_batch('_Test Batch Price Item', 400, 0)
args = frappe._dict({
"item_code": "_Test Batch Price Item",
"company": "_Test Company with perpetual inventory",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"customer": "_Test Customer",
"name": None
})
#test price for batch1
args.update({'batch_no': batch1})
details = get_item_details(args)
self.assertEqual(details.get('price_list_rate'), 200)
#test price for batch2
args.update({'batch_no': batch2})
details = get_item_details(args)
self.assertEqual(details.get('price_list_rate'), 300)
#test price for batch3
args.update({'batch_no': batch3})
details = get_item_details(args)
self.assertEqual(details.get('price_list_rate'), 400)
def create_batch(item_code, rate, create_item_price_for_batch):
pi = make_purchase_invoice(company="_Test Company with perpetual inventory",
warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1,
expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code)
batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
if not create_item_price_for_batch:
create_price_list_for_batch(item_code, None, rate)
else:
create_price_list_for_batch(item_code, batch, rate)
return batch
def create_price_list_for_batch(item_code, batch, rate):
frappe.get_doc({
'doctype': 'Item Price',
'item_code': '_Test Batch Price Item',
'price_list': '_Test Price List',
'batch_no': batch,
'price_list_rate': rate
}).insert()
def make_new_batch(**args):
args = frappe._dict(args)

View File

@ -106,9 +106,9 @@
"item_tax_section_break",
"taxes",
"inspection_criteria",
"quality_inspection_template",
"inspection_required_before_purchase",
"inspection_required_before_delivery",
"quality_inspection_template",
"manufacturing",
"default_bom",
"is_sub_contracted_item",
@ -814,7 +814,6 @@
"label": "Inspection Required before Delivery"
},
{
"depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
"fieldname": "quality_inspection_template",
"fieldtype": "Link",
"label": "Quality Inspection Template",
@ -1069,7 +1068,7 @@
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 1,
"modified": "2020-08-07 14:24:58.384992",
"modified": "2021-01-25 20:49:50.222976",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
@ -1131,4 +1130,4 @@
"sort_order": "DESC",
"title_field": "item_name",
"track_changes": 1
}
}

View File

@ -15,5 +15,13 @@ frappe.ui.form.on("Item Price", {
frm.set_df_property("bulk_import_help", "options",
'<a href="#data-import-tool/Item Price">' + __("Import in Bulk") + '</a>');
frm.set_query('batch_no', function() {
return {
filters: {
'item': frm.doc.item_code
}
}
});
}
});

View File

@ -18,6 +18,7 @@
"price_list",
"customer",
"supplier",
"batch_no",
"column_break_3",
"buying",
"selling",
@ -47,31 +48,41 @@
"oldfieldtype": "Select",
"options": "Item",
"reqd": 1,
"search_index": 1
"search_index": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "uom",
"fieldtype": "Link",
"label": "UOM",
"options": "UOM"
"options": "UOM",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"description": "Quantity that must be bought or sold per UOM",
"fieldname": "packing_unit",
"fieldtype": "Int",
"label": "Packing Unit"
"label": "Packing Unit",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_17",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "item_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Item Name",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fetch_from": "item_code.brand",
@ -79,19 +90,25 @@
"fieldtype": "Read Only",
"in_list_view": 1,
"label": "Brand",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "item_description",
"fieldtype": "Text",
"label": "Item Description",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "price_list_details",
"fieldtype": "Section Break",
"label": "Price List",
"options": "fa fa-tags"
"options": "fa fa-tags",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "price_list",
@ -100,7 +117,9 @@
"in_standard_filter": 1,
"label": "Price List",
"options": "Price List",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"bold": 1,
@ -108,37 +127,49 @@
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer"
"options": "Customer",
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.buying == 1",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier"
"options": "Supplier",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "buying",
"fieldtype": "Check",
"label": "Buying",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "selling",
"fieldtype": "Check",
"label": "Selling",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "item_details",
"fieldtype": "Section Break",
"options": "fa fa-tag"
"options": "fa fa-tag",
"show_days": 1,
"show_seconds": 1
},
{
"bold": 1,
@ -146,11 +177,15 @@
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "col_br_1",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "price_list_rate",
@ -162,53 +197,80 @@
"oldfieldname": "ref_rate",
"oldfieldtype": "Currency",
"options": "currency",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "section_break_15",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
},
{
"default": "Today",
"fieldname": "valid_from",
"fieldtype": "Date",
"label": "Valid From"
"label": "Valid From",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "lead_time_days",
"fieldtype": "Int",
"label": "Lead Time in days"
"label": "Lead Time in days",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "valid_upto",
"fieldtype": "Date",
"label": "Valid Upto"
"label": "Valid Upto",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "section_break_24",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "note",
"fieldtype": "Text",
"label": "Note"
"label": "Note",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "reference",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Reference"
"label": "Reference",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch",
"show_days": 1,
"show_seconds": 1
}
],
"icon": "fa fa-flag",
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-07-06 22:31:32.943475",
"modified": "2020-12-08 18:12:15.395772",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Price",

View File

@ -54,7 +54,8 @@ class ItemPrice(Document):
"valid_upto",
"packing_unit",
"customer",
"supplier",]:
"supplier",
"batch_no"]:
if self.get(field):
conditions += " and {0} = %({0})s ".format(field)
else:
@ -68,7 +69,7 @@ class ItemPrice(Document):
self.as_dict(),)
if price_list_rate:
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
def before_save(self):
if self.selling:

View File

@ -1,9 +1,10 @@
frappe.listview_settings['Material Request'] = {
add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
get_indicator: function(doc) {
if(doc.status=="Stopped") {
var precision = frappe.defaults.get_default("float_precision");
if (doc.status=="Stopped") {
return [__("Stopped"), "red", "status,=,Stopped"];
} else if(doc.transfer_status && doc.docstatus != 2) {
} else if (doc.transfer_status && doc.docstatus != 2) {
if (doc.transfer_status == "Not Started") {
return [__("Not Started"), "orange"];
} else if (doc.transfer_status == "In Transit") {
@ -11,14 +12,14 @@ frappe.listview_settings['Material Request'] = {
} else if (doc.transfer_status == "Completed") {
return [__("Completed"), "green"];
}
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) {
} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) {
return [__("Pending"), "orange", "per_ordered,=,0"];
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) {
return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) {
} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) {
if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) {
return [__("Partially Received"), "yellow", "per_received,<,100"];
} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) {
} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) {
return [__("Received"), "green", "per_received,=,100"];
} else if (doc.material_request_type == "Purchase") {
return [__("Ordered"), "green", "per_ordered,=,100"];

View File

@ -5,6 +5,14 @@ frappe.provide("erpnext.accounts.dimensions");
frappe.ui.form.on('Stock Entry', {
setup: function(frm) {
frm.set_indicator_formatter('item_code', function(doc) {
if (!doc.s_warehouse) {
return 'blue';
} else {
return (doc.qty<=doc.actual_qty) ? 'green' : 'orange';
}
});
frm.set_query('work_order', function() {
return {
filters: [
@ -779,15 +787,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}
}
this.frm.set_indicator_formatter('item_code',
function(doc) {
if (!doc.s_warehouse) {
return 'blue';
} else {
return (doc.qty<=doc.actual_qty) ? "green" : "orange"
}
})
this.frm.add_fetch("purchase_order", "supplier", "supplier");
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }

View File

@ -16,6 +16,7 @@
"action_if_quality_inspection_is_not_submitted",
"show_barcode_field",
"clean_description_html",
"disable_serial_no_and_batch_selector",
"section_break_7",
"auto_insert_price_list_rate_if_missing",
"allow_negative_stock",
@ -227,6 +228,12 @@
"fieldname": "control_historical_stock_transactions_section",
"fieldtype": "Section Break",
"label": "Control Historical Stock Transactions"
},
{
"default": "0",
"fieldname": "disable_serial_no_and_batch_selector",
"fieldtype": "Check",
"label": "Disable Serial No And Batch Selector"
}
],
"icon": "icon-cog",
@ -234,7 +241,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2020-12-29 12:53:31.162247",
"modified": "2021-01-18 13:15:38.352796",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",

View File

@ -674,6 +674,8 @@ def get_item_price(args, item_code, ignore_party=False):
and price_list=%(price_list)s
and ifnull(uom, '') in ('', %(uom)s)"""
conditions += "and ifnull(batch_no, '') in ('', %(batch_no)s)"
if not ignore_party:
if args.get("customer"):
conditions += " and customer=%(customer)s"
@ -692,7 +694,7 @@ def get_item_price(args, item_code, ignore_party=False):
return frappe.db.sql(""" select name, price_list_rate, uom
from `tabItem Price` {conditions}
order by valid_from desc, uom desc """.format(conditions=conditions), args)
order by valid_from desc, batch_no desc, uom desc """.format(conditions=conditions), args)
def get_price_list_rate_for(args, item_code):
"""
@ -711,6 +713,7 @@ def get_price_list_rate_for(args, item_code):
"uom": args.get('uom'),
"transaction_date": args.get('transaction_date'),
"posting_date": args.get('posting_date'),
"batch_no": args.get('batch_no')
}
item_price_data = 0

View File

@ -40,7 +40,7 @@
<div class="col-md-{{ section.column_value }} mb-4">
<div class="card h-100 justify-content-between">
{% if card.image %}
<div class="website-image-lazy" data-class="card-img-top h-100" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
<div class="website-image-lazy" data-class="card-img-top h-75" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ card.title }}</h5>