mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-18 14:25:10 +00:00
Merge branch 'v12-pre-release' into version-12
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '12.9.4'
|
||||
__version__ = '12.10.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
|
||||
on_change: function() {
|
||||
var me = frappe.treeview_settings['Account'].treeview;
|
||||
var company = me.page.fields_dict.company.get_value();
|
||||
if (!company) {
|
||||
frappe.throw(__("Please set a Company"));
|
||||
}
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.account.account.get_root_company",
|
||||
args: {
|
||||
|
||||
@@ -41,10 +41,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
|
||||
|
||||
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule:rule.min_spent, reverse=True)
|
||||
# sort collection rule, first item on list will be lowest min_spent
|
||||
tier_spent_level = sorted(
|
||||
[d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule: rule.min_spent, reverse=False,
|
||||
)
|
||||
|
||||
# looping and apply tier from lowest min_spent
|
||||
for i, d in enumerate(tier_spent_level):
|
||||
if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
|
||||
# if cumulative spend more than min_spent then continue to next tier
|
||||
if (lp_details.total_spent + current_transaction_amount) >= d.min_spent:
|
||||
lp_details.tier_name = d.tier_name
|
||||
lp_details.collection_factor = d.collection_factor
|
||||
else:
|
||||
|
||||
@@ -68,6 +68,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
|
||||
lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer})
|
||||
|
||||
customer.load_from_db()
|
||||
self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
|
||||
self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
|
||||
self.assertEqual(lpe.loyalty_points, earned_points)
|
||||
|
||||
@@ -227,6 +227,8 @@ class PaymentEntry(AccountsController):
|
||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||
elif self.party_type == "Employee":
|
||||
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
|
||||
elif self.party_type == "Shareholder":
|
||||
valid_reference_doctypes = ("Journal Entry")
|
||||
|
||||
for d in self.get("references"):
|
||||
if not d.allocated_amount:
|
||||
@@ -316,7 +318,7 @@ class PaymentEntry(AccountsController):
|
||||
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||
invoice_payment_amount_map[key] += reference.allocated_amount
|
||||
|
||||
if not invoice_paid_amount_map.get(reference.reference_name):
|
||||
if not invoice_paid_amount_map.get(key):
|
||||
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
||||
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
||||
for term in payment_schedule:
|
||||
@@ -329,12 +331,14 @@ class PaymentEntry(AccountsController):
|
||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||
else:
|
||||
outstanding = invoice_paid_amount_map.get(key)['outstanding']
|
||||
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
||||
|
||||
if amount > outstanding:
|
||||
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||
|
||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||
if amount and outstanding:
|
||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 2:
|
||||
@@ -1088,17 +1092,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||
references = []
|
||||
for payment_term in payment_schedule:
|
||||
references.append({
|
||||
'reference_doctype': dt,
|
||||
'reference_name': dn,
|
||||
'bill_no': doc.get('bill_no'),
|
||||
'due_date': doc.get('due_date'),
|
||||
'total_amount': grand_total,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'payment_term': payment_term.payment_term,
|
||||
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount,
|
||||
payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
|
||||
payment_term.precision('payment_amount'))
|
||||
})
|
||||
|
||||
if payment_term_outstanding:
|
||||
references.append({
|
||||
'reference_doctype': dt,
|
||||
'reference_name': dn,
|
||||
'bill_no': doc.get('bill_no'),
|
||||
'due_date': doc.get('due_date'),
|
||||
'total_amount': grand_total,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'payment_term': payment_term.payment_term,
|
||||
'allocated_amount': payment_term_outstanding
|
||||
})
|
||||
|
||||
return references
|
||||
|
||||
|
||||
@@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase):
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.items[1].is_free_item, 1)
|
||||
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
||||
|
||||
def test_cumulative_pricing_rule(self):
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
|
||||
test_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Cumulative Pricing Rule",
|
||||
"apply_on": "Item Code",
|
||||
"currency": "USD",
|
||||
"items": [{
|
||||
"item_code": "_Test Item",
|
||||
}],
|
||||
"is_cumulative": 1,
|
||||
"selling": 1,
|
||||
"applicable_for": "Customer",
|
||||
"customer": "_Test Customer",
|
||||
"rate_or_discount": "Discount Percentage",
|
||||
"rate": 0,
|
||||
"min_amt": 0,
|
||||
"max_amt": 10000,
|
||||
"discount_percentage": 17.5,
|
||||
"price_or_product_discount": "Price",
|
||||
"company": "_Test Company",
|
||||
"valid_from": frappe.utils.nowdate(),
|
||||
"valid_upto": frappe.utils.nowdate()
|
||||
}
|
||||
frappe.get_doc(test_record.copy()).insert()
|
||||
|
||||
args = frappe._dict({
|
||||
"item_code": "_Test Item",
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "_Test Currency",
|
||||
"doctype": "Sales Invoice",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"name": None,
|
||||
"transaction_date": frappe.utils.nowdate()
|
||||
})
|
||||
details = get_item_details(args)
|
||||
|
||||
self.assertTrue(details)
|
||||
|
||||
def make_pricing_rule(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -13,7 +13,10 @@ from six import string_types
|
||||
import frappe
|
||||
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
|
||||
from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||
from erpnext.setup.doctype.brand.brand import get_brand_defaults
|
||||
from frappe import _, throw
|
||||
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
|
||||
|
||||
@@ -366,8 +369,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
||||
sum_qty, sum_amt = [0, 0]
|
||||
doctype = doc.get('parenttype') or doc.doctype
|
||||
|
||||
date_field = ('transaction_date'
|
||||
if doc.get('transaction_date') else 'posting_date')
|
||||
date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date'
|
||||
|
||||
child_doctype = '{0} Item'.format(doctype)
|
||||
apply_on = frappe.scrub(pr_doc.get('apply_on'))
|
||||
@@ -481,6 +483,14 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
if item_details.get("parenttype") == 'Sales Order':
|
||||
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||
|
||||
company = args.get('company') or doc.company
|
||||
item_details.free_item_data['income_account'] = get_default_income_account(
|
||||
args=args,
|
||||
item=get_item_defaults(free_item, company),
|
||||
item_group=get_item_group_defaults(free_item, company),
|
||||
brand=get_brand_defaults(free_item, company),
|
||||
)
|
||||
|
||||
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||
if pricing_rule_args.get('item_code'):
|
||||
items = [d.item_code for d in doc.items
|
||||
|
||||
@@ -552,6 +552,8 @@ class SalesInvoice(SellingController):
|
||||
continue
|
||||
|
||||
for d in self.get('items'):
|
||||
if not d.item_code: continue
|
||||
|
||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||
if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1)
|
||||
@@ -574,14 +576,14 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def validate_item_code(self):
|
||||
for d in self.get('items'):
|
||||
if not d.item_code:
|
||||
if not d.item_code and self.is_opening == "No":
|
||||
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
|
||||
|
||||
def validate_warehouse(self):
|
||||
super(SalesInvoice, self).validate_warehouse()
|
||||
|
||||
for d in self.get_item_list():
|
||||
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||
if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
||||
|
||||
def validate_delivery_note(self):
|
||||
|
||||
@@ -45,7 +45,9 @@ class ShippingRule(Document):
|
||||
shipping_amount = 0.0
|
||||
by_value = False
|
||||
|
||||
self.validate_countries(doc)
|
||||
if doc.get_shipping_address():
|
||||
# validate country only if there is address
|
||||
self.validate_countries(doc)
|
||||
|
||||
if self.calculate_based_on == 'Net Total':
|
||||
value = doc.base_net_total
|
||||
|
||||
@@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
||||
"rate": tax_rate_detail.tax_withholding_rate,
|
||||
"threshold": tax_rate_detail.single_threshold,
|
||||
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
|
||||
"description": tax_withholding.category_name
|
||||
"description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
|
||||
})
|
||||
|
||||
def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
||||
@@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
|
||||
if company:
|
||||
condition += "and company =%s" % (company)
|
||||
if from_date and to_date:
|
||||
condition += "and posting_date between %s and %s" % (company, from_date, to_date)
|
||||
condition += "and posting_date between %s and %s" % (from_date, to_date)
|
||||
|
||||
## Appending the same supplier again if length of suppliers list is 1
|
||||
## since tuple of single element list contains None, For example ('Test Supplier 1', )
|
||||
@@ -225,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount,
|
||||
certificate_limit > deducted_amount):
|
||||
valid = True
|
||||
|
||||
return valid
|
||||
return valid
|
||||
|
||||
@@ -605,10 +605,14 @@ def get_party_shipping_address(doctype, name):
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None):
|
||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None):
|
||||
cond = "1=1"
|
||||
if posting_date:
|
||||
cond = "posting_date <= '{0}'".format(posting_date)
|
||||
if future_payment:
|
||||
cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date)
|
||||
else:
|
||||
cond = "posting_date <= '{0}'".format(posting_date)
|
||||
|
||||
if company:
|
||||
cond += "and company = '{0}'".format(company)
|
||||
|
||||
|
||||
@@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Payable', 9);
|
||||
|
||||
|
||||
@@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
|
||||
|
||||
|
||||
@@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Receivable', 9);
|
||||
|
||||
|
||||
@@ -169,9 +169,11 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def append_subtotal_row(self, party):
|
||||
sub_total_row = self.total_row_map.get(party)
|
||||
self.data.append(sub_total_row)
|
||||
self.data.append({})
|
||||
self.update_sub_total_row(sub_total_row, 'Total')
|
||||
|
||||
if sub_total_row:
|
||||
self.data.append(sub_total_row)
|
||||
self.data.append({})
|
||||
self.update_sub_total_row(sub_total_row, 'Total')
|
||||
|
||||
def get_voucher_balance(self, gle):
|
||||
if self.filters.get("sales_person"):
|
||||
@@ -232,7 +234,8 @@ class ReceivablePayableReport(object):
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.append_subtotal_row(self.previous_party)
|
||||
self.data.append(self.total_row_map.get('Total'))
|
||||
if self.data:
|
||||
self.data.append(self.total_row_map.get('Total'))
|
||||
|
||||
def append_row(self, row):
|
||||
self.allocate_future_payments(row)
|
||||
@@ -534,7 +537,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def get_ageing_data(self, entry_date, row):
|
||||
# [0-30, 30-60, 60-90, 90-120, 120-above]
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
|
||||
|
||||
if not (self.age_as_on and entry_date):
|
||||
return
|
||||
@@ -559,6 +562,14 @@ class ReceivablePayableReport(object):
|
||||
conditions, values = self.prepare_conditions()
|
||||
order_by = self.get_order_by_condition()
|
||||
|
||||
if self.filters.show_future_payments:
|
||||
values.insert(2, self.filters.report_date)
|
||||
|
||||
date_condition = """AND (posting_date <= %s
|
||||
OR (against_voucher IS NULL AND DATE(creation) <= %s))"""
|
||||
else:
|
||||
date_condition = "AND posting_date <=%s"
|
||||
|
||||
if self.filters.get(scrub(self.party_type)):
|
||||
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
||||
else:
|
||||
@@ -574,9 +585,8 @@ class ReceivablePayableReport(object):
|
||||
docstatus < 2
|
||||
and party_type=%s
|
||||
and (party is not null and party != '')
|
||||
and posting_date <= %s
|
||||
{1} {2}"""
|
||||
.format(select_fields, conditions, order_by), values, as_dict=True)
|
||||
{1} {2} {3}"""
|
||||
.format(select_fields, date_condition, conditions, order_by), values, as_dict=True)
|
||||
|
||||
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||
if self.filters.get("sales_person"):
|
||||
|
||||
@@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"fieldname":"based_on_payment_terms",
|
||||
"label": __("Based On Payment Terms"),
|
||||
"fieldtype": "Check",
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"show_future_payments",
|
||||
"label": __("Show Future Payments"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
],
|
||||
|
||||
onload: function(report) {
|
||||
@@ -122,11 +127,4 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Receivable Summary', 9);
|
||||
|
||||
@@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
self.get_party_total(args)
|
||||
|
||||
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
||||
self.filters.report_date, self.filters.company) or {}
|
||||
self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
|
||||
|
||||
for party, party_dict in iteritems(self.party_total):
|
||||
if party_dict.outstanding == 0:
|
||||
|
||||
@@ -93,7 +93,7 @@ def get_assets(filters):
|
||||
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||
from (SELECT a.asset_category,
|
||||
ifnull(sum(case when ds.schedule_date < %(from_date)s then
|
||||
ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
@@ -111,13 +111,11 @@ def get_assets(filters):
|
||||
0
|
||||
end), 0) as depreciation_amount_during_the_period
|
||||
from `tabAsset` a, `tabDepreciation Schedule` ds
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
|
||||
group by a.asset_category
|
||||
union
|
||||
SELECT a.asset_category,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
|
||||
then
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
|
||||
0
|
||||
else
|
||||
a.opening_accumulated_depreciation
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Balance Sheet', 10);
|
||||
|
||||
frappe.query_reports["Balance Sheet"]["filters"].push({
|
||||
"fieldname": "accumulated_values",
|
||||
"label": __("Accumulated Values"),
|
||||
|
||||
@@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Cash Flow"] = $.extend({},
|
||||
erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Cash Flow', 10);
|
||||
|
||||
// The last item in the array is the definition for Presentation Currency
|
||||
// filter. It won't be used in cash flow for now so we pop it. Please take
|
||||
// of this if you are working here.
|
||||
|
||||
@@ -50,9 +50,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
|
||||
to_date = add_months(start_date, months_to_add)
|
||||
start_date = to_date
|
||||
|
||||
if to_date == get_first_day(to_date):
|
||||
# if to_date is the first day, get the last day of previous month
|
||||
to_date = add_days(to_date, -1)
|
||||
# Subtract one day from to_date, as it may be first day in next fiscal year or month
|
||||
to_date = add_days(to_date, -1)
|
||||
|
||||
if to_date <= year_end_date:
|
||||
# the normal case
|
||||
|
||||
@@ -160,12 +160,5 @@ frappe.query_reports["General Ledger"] = {
|
||||
]
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('General Ledger', 15)
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Profit and Loss Statement"] = $.extend({},
|
||||
erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
|
||||
|
||||
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
|
||||
{
|
||||
"fieldname": "project",
|
||||
|
||||
@@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = {
|
||||
]
|
||||
}
|
||||
|
||||
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"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Purchase Register', 7);
|
||||
@@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = {
|
||||
]
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Sales Register', 7);
|
||||
|
||||
|
||||
@@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
"initial_depth": 3
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Trial Balance', 6);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', {
|
||||
return indicator;
|
||||
}
|
||||
);
|
||||
|
||||
frm.set_query('select_serial_no', function(doc){
|
||||
return {
|
||||
asset: frm.doc.asset_name
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
select_serial_no: (frm) => {
|
||||
let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no;
|
||||
if (serial_nos) {
|
||||
serial_nos = serial_nos.split('\n');
|
||||
serial_nos.push(frm.doc.select_serial_no);
|
||||
|
||||
const unique_sn = serial_nos.filter(function(elem, index, self) {
|
||||
return index === self.indexOf(elem);
|
||||
});
|
||||
|
||||
frm.set_value("serial_no", unique_sn.join('\n'));
|
||||
}
|
||||
},
|
||||
|
||||
refresh: (frm) => {
|
||||
@@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', {
|
||||
},
|
||||
end_date: (frm, cdt, cdn) => {
|
||||
get_next_due_date(frm, cdt, cdn);
|
||||
},
|
||||
assign_to: (frm, cdt, cdn) => {
|
||||
var d = locals[cdt][cdn];
|
||||
if (frm.doc.__islocal) {
|
||||
frappe.model.set_value(cdt, cdn, "assign_to", "");
|
||||
frappe.model.set_value(cdt, cdn, "assign_to_name", "");
|
||||
frappe.throw(__("Please save before assigning task."));
|
||||
}
|
||||
if (d.assign_to) {
|
||||
return frappe.call({
|
||||
method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks',
|
||||
args: {
|
||||
asset_maintenance_name: frm.doc.name,
|
||||
assign_to_member: d.assign_to,
|
||||
maintenance_task: d.maintenance_task,
|
||||
next_due_date: d.next_due_date
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,559 +1,140 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:asset_name",
|
||||
"beta": 0,
|
||||
"creation": "2017-10-19 16:50:22.879545",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "field:asset_name",
|
||||
"creation": "2017-10-19 16:50:22.879545",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"asset_name",
|
||||
"asset_category",
|
||||
"company",
|
||||
"column_break_3",
|
||||
"item_code",
|
||||
"item_name",
|
||||
"section_break_6",
|
||||
"maintenance_team",
|
||||
"column_break_9",
|
||||
"maintenance_manager",
|
||||
"maintenance_manager_name",
|
||||
"section_break_8",
|
||||
"asset_maintenance_tasks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Asset Name",
|
||||
"options": "Asset",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.asset_category",
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Category",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Asset Category"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Code"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.item_name",
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_serial_no",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Select Serial No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Serial No",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "serial_no",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Serial No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_team",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Maintenance Team",
|
||||
"options": "Asset Maintenance Team",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "maintenance_team",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Team",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance Team",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "maintenance_team.maintenance_manager",
|
||||
"fieldname": "maintenance_manager",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Manager",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_manager",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Maintenance Manager",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "maintenance_team.maintenance_manager_name",
|
||||
"fieldname": "maintenance_manager_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Manager Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_manager_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Maintenance Manager Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Tasks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tasks"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "asset_maintenance_tasks",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Tasks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance Task",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "asset_maintenance_tasks",
|
||||
"fieldtype": "Table",
|
||||
"label": "Maintenance Tasks",
|
||||
"options": "Asset Maintenance Task",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-05-22 17:20:54.711885",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-28 20:28:32.993823",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Quality Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Quality Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -16,12 +16,11 @@ class AssetMaintenance(Document):
|
||||
throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task))
|
||||
if getdate(task.next_due_date) < getdate(nowdate()):
|
||||
task.maintenance_status = "Overdue"
|
||||
if not task.assign_to and self.docstatus == 0:
|
||||
throw(_("Row #{}: Please asign task to a member.").format(task.idx))
|
||||
|
||||
def on_update(self):
|
||||
for task in self.get('asset_maintenance_tasks'):
|
||||
if not task.assign_to:
|
||||
task.db_set("assign_to", self.maintenance_manager)
|
||||
task.db_set("assign_to_name", self.maintenance_manager_name)
|
||||
assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
|
||||
self.sync_maintenance_tasks()
|
||||
|
||||
@@ -39,7 +38,7 @@ class AssetMaintenance(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date):
|
||||
team_member = frappe.get_doc('User', assign_to_member).email
|
||||
team_member = frappe.db.get_value('User', assign_to_member, "email")
|
||||
args = {
|
||||
'doctype' : 'Asset Maintenance',
|
||||
'assign_to' : team_member,
|
||||
@@ -78,7 +77,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las
|
||||
|
||||
def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance,
|
||||
"task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])})
|
||||
"task": task.name, "maintenance_status": ('in',['Planned','Overdue'])})
|
||||
|
||||
if not asset_maintenance_log:
|
||||
asset_maintenance_log = frappe.get_doc({
|
||||
@@ -87,7 +86,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
"asset_name": asset_maintenance,
|
||||
"item_code": item_code,
|
||||
"item_name": item_name,
|
||||
"task": task.maintenance_task,
|
||||
"task": task.name,
|
||||
"has_certificate": task.certificate_required,
|
||||
"description": task.description,
|
||||
"assign_to_name": task.assign_to_name,
|
||||
@@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_team_members(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")})
|
||||
return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") })
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_maintenance_log(asset_name):
|
||||
|
||||
@@ -125,13 +125,15 @@ def get_maintenance_tasks():
|
||||
"start_date": nowdate(),
|
||||
"periodicity": "Monthly",
|
||||
"maintenance_type": "Preventive Maintenance",
|
||||
"maintenance_status": "Planned"
|
||||
"maintenance_status": "Planned",
|
||||
"assign_to": "marcus@abc.com"
|
||||
},
|
||||
{"maintenance_task": "Check Gears",
|
||||
"start_date": nowdate(),
|
||||
"periodicity": "Yearly",
|
||||
"maintenance_type": "Calibration",
|
||||
"maintenance_status": "Planned"
|
||||
"maintenance_status": "Planned",
|
||||
"assign_to": "thalia@abc.com"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,819 +1,210 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "naming_series:",
|
||||
"beta": 0,
|
||||
"creation": "2017-10-23 16:58:44.424309",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2017-10-23 16:58:44.424309",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"asset_maintenance",
|
||||
"naming_series",
|
||||
"asset_name",
|
||||
"column_break_2",
|
||||
"item_code",
|
||||
"item_name",
|
||||
"section_break_5",
|
||||
"task",
|
||||
"task_name",
|
||||
"maintenance_type",
|
||||
"periodicity",
|
||||
"assign_to_name",
|
||||
"column_break_6",
|
||||
"due_date",
|
||||
"completion_date",
|
||||
"maintenance_status",
|
||||
"section_break_12",
|
||||
"has_certificate",
|
||||
"certificate_attachement",
|
||||
"section_break_6",
|
||||
"description",
|
||||
"column_break_9",
|
||||
"actions_performed",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "asset_maintenance",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Maintenance",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "asset_maintenance",
|
||||
"fieldtype": "Link",
|
||||
"label": "Asset Maintenance",
|
||||
"options": "Asset Maintenance"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "ACC-AML-.YYYY.-",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"options": "ACC-AML-.YYYY.-",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_maintenance.asset_name",
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "asset_maintenance.asset_name",
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Asset Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_maintenance.item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "asset_maintenance.item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Code"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_maintenance.item_name",
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "asset_maintenance.item_name",
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "task",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Task",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance Task",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "task",
|
||||
"fieldtype": "Link",
|
||||
"label": "Task",
|
||||
"options": "Asset Maintenance Task"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.maintenance_type",
|
||||
"fieldname": "maintenance_type",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "task.maintenance_type",
|
||||
"fieldname": "maintenance_type",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Maintenance Type"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.periodicity",
|
||||
"fieldname": "periodicity",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Periodicity",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "task.periodicity",
|
||||
"fieldname": "periodicity",
|
||||
"fieldtype": "Data",
|
||||
"label": "Periodicity",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.assign_to_name",
|
||||
"fieldname": "assign_to_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Assign To",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "task.assign_to_name",
|
||||
"fieldname": "assign_to_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Assign To"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.next_due_date",
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "task.next_due_date",
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Due Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "completion_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Completion Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "completion_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Completion Date"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "maintenance_status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Maintenance Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Planned\nCompleted\nCancelled\nOverdue",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_status",
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Maintenance Status",
|
||||
"options": "Planned\nCompleted\nCancelled\nOverdue",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_12",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_12",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.certificate_required",
|
||||
"fieldname": "has_certificate",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Has Certificate ",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"fetch_from": "task.certificate_required",
|
||||
"fieldname": "has_certificate",
|
||||
"fieldtype": "Check",
|
||||
"label": "Has Certificate "
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.has_certificate",
|
||||
"fieldname": "certificate_attachement",
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Certificate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "eval:doc.has_certificate",
|
||||
"fieldname": "certificate_attachement",
|
||||
"fieldtype": "Attach",
|
||||
"label": "Certificate"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "task.description",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "task.description",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Description",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "actions_performed",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Actions performed",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "actions_performed",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Actions performed"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Asset Maintenance Log",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Asset Maintenance Log",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "task.maintenance_task",
|
||||
"fieldname": "task_name",
|
||||
"fieldtype": "Data",
|
||||
"in_preview": 1,
|
||||
"label": "Task Name",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:51.457835",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance Log",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-28 20:51:48.238397",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1,
|
||||
"track_views": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
@@ -7,5 +7,4 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AssetMaintenanceTask(Document):
|
||||
def autoname(self):
|
||||
self.name = self.maintenance_task
|
||||
pass
|
||||
|
||||
@@ -1056,7 +1056,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-17 13:04:28.185197",
|
||||
"modified": "2020-06-12 14:08:11.777120",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
@@ -1100,6 +1100,12 @@
|
||||
"read": 1,
|
||||
"role": "Purchase Manager",
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Accounts User"
|
||||
}
|
||||
],
|
||||
"search_fields": "status, transaction_date, supplier,grand_total",
|
||||
|
||||
@@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController):
|
||||
"compare_fields": [["project", "="], ["item_code", "="],
|
||||
["uom", "="], ["conversion_factor", "="]],
|
||||
"is_child_table": True
|
||||
},
|
||||
"Material Request": {
|
||||
"ref_dn_field": "material_request",
|
||||
"compare_fields": [["company", "="]],
|
||||
},
|
||||
"Material Request Item": {
|
||||
"ref_dn_field": "material_request_item",
|
||||
"compare_fields": [["project", "="], ["item_code", "="]],
|
||||
"is_child_table": True
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
@@ -142,7 +142,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(len(po.get('items')), 2)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
@@ -183,6 +183,23 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(len(po.get('items')), 1)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
def test_update_child_qty_rate_perm(self):
|
||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
test_user.add_roles("Accounts User")
|
||||
frappe.set_user(user)
|
||||
|
||||
# update qty
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
|
||||
|
||||
# add new item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns(filters)
|
||||
@@ -54,15 +55,16 @@ def get_columns(filters):
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Description"),
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Data",
|
||||
"width": 200
|
||||
"label": _("Item"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Quantity"),
|
||||
"fieldname": "quantity",
|
||||
"fieldtype": "Int",
|
||||
"fieldtype": "Float",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
@@ -118,7 +120,7 @@ def get_columns(filters):
|
||||
},
|
||||
{
|
||||
"label": _("Purchase Order Amount(Company Currency)"),
|
||||
"fieldname": "purchase_order_amt_usd",
|
||||
"fieldname": "purchase_order_amt_in_company_currency",
|
||||
"fieldtype": "Float",
|
||||
"width": 140
|
||||
},
|
||||
@@ -175,17 +177,17 @@ def get_data(filters):
|
||||
"requesting_site": po.warehouse,
|
||||
"requestor": po.owner,
|
||||
"material_request_no": po.material_request,
|
||||
"description": po.description,
|
||||
"quantity": po.qty,
|
||||
"item_code": po.item_code,
|
||||
"quantity": flt(po.qty),
|
||||
"unit_of_measurement": po.stock_uom,
|
||||
"status": po.status,
|
||||
"purchase_order_date": po.transaction_date,
|
||||
"purchase_order": po.parent,
|
||||
"supplier": po.supplier,
|
||||
"estimated_cost": mr_record.get('amount'),
|
||||
"actual_cost": pi_records.get(po.name),
|
||||
"purchase_order_amt": po.amount,
|
||||
"purchase_order_amt_in_company_currency": po.base_amount,
|
||||
"estimated_cost": flt(mr_record.get('amount')),
|
||||
"actual_cost": flt(pi_records.get(po.name)),
|
||||
"purchase_order_amt": flt(po.amount),
|
||||
"purchase_order_amt_in_company_currency": flt(po.base_amount),
|
||||
"expected_delivery_date": po.schedule_date,
|
||||
"actual_delivery_date": pr_records.get(po.name)
|
||||
}
|
||||
@@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions):
|
||||
SELECT
|
||||
par.transaction_date,
|
||||
par.per_ordered,
|
||||
par.owner,
|
||||
child.name,
|
||||
child.parent,
|
||||
child.amount
|
||||
child.amount,
|
||||
child.qty,
|
||||
child.item_code,
|
||||
child.uom,
|
||||
par.status
|
||||
FROM `tabMaterial Request` par, `tabMaterial Request Item` child
|
||||
WHERE
|
||||
par.per_ordered>=0
|
||||
@@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions):
|
||||
procurement_record_details = dict(
|
||||
material_request_date=record.transaction_date,
|
||||
material_request_no=record.parent,
|
||||
estimated_cost=record.amount
|
||||
requestor=record.owner,
|
||||
item_code=record.item_code,
|
||||
estimated_cost=flt(record.amount),
|
||||
quantity=flt(record.qty),
|
||||
unit_of_measurement=record.uom,
|
||||
status=record.status,
|
||||
actual_cost=0,
|
||||
purchase_order_amt=0,
|
||||
purchase_order_amt_in_company_currency=0
|
||||
)
|
||||
procurement_record_against_mr.append(procurement_record_details)
|
||||
return mr_records, procurement_record_against_mr
|
||||
@@ -259,7 +274,7 @@ def get_po_entries(conditions):
|
||||
child.warehouse,
|
||||
child.material_request,
|
||||
child.material_request_item,
|
||||
child.description,
|
||||
child.item_code,
|
||||
child.stock_uom,
|
||||
child.qty,
|
||||
child.amount,
|
||||
|
||||
@@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase):
|
||||
def test_result_for_procurement_tracker(self):
|
||||
filters = {
|
||||
'company': '_Test Procurement Company',
|
||||
'cost_center': '_Test Cost Center - _TC'
|
||||
'cost_center': 'Main - _TPC'
|
||||
}
|
||||
expected_data = self.generate_expected_data()
|
||||
report = execute(filters)
|
||||
@@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase):
|
||||
country="Pakistan"
|
||||
)).insert()
|
||||
warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
|
||||
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse)
|
||||
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC")
|
||||
po = make_purchase_order(mr.name)
|
||||
po.supplier = "_Test Supplier"
|
||||
po.get("items")[0].cost_center = "_Test Cost Center - _TC"
|
||||
po.get("items")[0].cost_center = "Main - _TPC"
|
||||
po.submit()
|
||||
pr = make_purchase_receipt(po.name)
|
||||
pr.get("items")[0].cost_center = "Main - _TPC"
|
||||
pr.submit()
|
||||
frappe.db.commit()
|
||||
date_obj = datetime.date(datetime.now())
|
||||
|
||||
po.load_from_db()
|
||||
|
||||
expected_data = {
|
||||
"material_request_date": date_obj,
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"cost_center": "Main - _TPC",
|
||||
"project": None,
|
||||
"requesting_site": "_Test Procurement Warehouse - _TPC",
|
||||
"requestor": "Administrator",
|
||||
"material_request_no": mr.name,
|
||||
"description": '_Test Item 1',
|
||||
"item_code": '_Test Item',
|
||||
"quantity": 10.0,
|
||||
"unit_of_measurement": "_Test UOM",
|
||||
"status": "To Bill",
|
||||
@@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase):
|
||||
"purchase_order": po.name,
|
||||
"supplier": "_Test Supplier",
|
||||
"estimated_cost": 0.0,
|
||||
"actual_cost": None,
|
||||
"purchase_order_amt": 5000.0,
|
||||
"purchase_order_amt_in_company_currency": 300000.0,
|
||||
"actual_cost": 0.0,
|
||||
"purchase_order_amt": po.net_total,
|
||||
"purchase_order_amt_in_company_currency": po.base_net_total,
|
||||
"expected_delivery_date": date_obj,
|
||||
"actual_delivery_date": date_obj
|
||||
}
|
||||
|
||||
54
erpnext/change_log/v12/v12_10_0.md
Normal file
54
erpnext/change_log/v12/v12_10_0.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## ERPNext v12.10.0 Release Note
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Create Shipping Address and Contact in ERPNext from the woocommerece platform ([#21654](https://github.com/frappe/erpnext/pull/21654))
|
||||
- Dashboard in Course and Assessment Plan ([#21889](https://github.com/frappe/erpnext/pull/21889))
|
||||
- Added conversion factor for pound, gram to Ounce ([#21710](https://github.com/frappe/erpnext/pull/21710))
|
||||
- Allowed renaming for Sales Stage ([#21800](https://github.com/frappe/erpnext/pull/21800))
|
||||
- Add filter for cost center in expense table ([#22209](https://github.com/frappe/erpnext/pull/22209))
|
||||
- Project filter in Trial Balance Report ([#21815](https://github.com/frappe/erpnext/pull/21815))
|
||||
- Added column Expired Leave in Leave Application dashboard ([#21859](https://github.com/frappe/erpnext/pull/21859))
|
||||
- Auto set serial nos and batches only if allowed in Stock Settings ([#21779](https://github.com/frappe/erpnext/pull/21779))
|
||||
- Filter batches based on selected item and warehouse in Pick List ([#21778](https://github.com/frappe/erpnext/pull/21778))
|
||||
- Errored documents handling while migrating data from Tally ([#22079](https://github.com/frappe/erpnext/pull/22079))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Routing operations table is blank on pull of operations in BOM ([#22040](https://github.com/frappe/erpnext/pull/22040))
|
||||
- Cannot make payment entry against shareholder ([#21597](https://github.com/frappe/erpnext/pull/21597))
|
||||
- Do not add filters in report on accounting dimension creation if it already exists ([#21941](https://github.com/frappe/erpnext/pull/21941))
|
||||
- Post Dated unallocated amount not considered in Advance Amount in AR/AP summary ([#21838](https://github.com/frappe/erpnext/pull/21838))
|
||||
- Check for Company before rendering tree in Account Tree ([#22204](https://github.com/frappe/erpnext/pull/22204))
|
||||
- Loyalty point entry use wrong tier ([#22168](https://github.com/frappe/erpnext/pull/22168))
|
||||
- Added Inactive serial no status ([#21849](https://github.com/frappe/erpnext/pull/21849))
|
||||
- Routing operations not added sequentially in the BOM ([#22110](https://github.com/frappe/erpnext/pull/22110))
|
||||
- TDS computation summary report ([#21987](https://github.com/frappe/erpnext/pull/21987))
|
||||
- Validate Payment Gateway only if it exists in Payment Request. ([#21806](https://github.com/frappe/erpnext/pull/21806))
|
||||
- Finished Product Valuation at Repack ([#22148](https://github.com/frappe/erpnext/pull/22148))
|
||||
- Wrong Ordered-Status Indicator for Material Request Items ([#22117](https://github.com/frappe/erpnext/pull/22117))
|
||||
- In-state Invoice not appearing in GSTR-1 report (India) ([#21787](https://github.com/frappe/erpnext/pull/21787))
|
||||
- Tax amount in GSTR-1 JSON (India) ([#21791](https://github.com/frappe/erpnext/pull/21791))
|
||||
- Fetch depreciation amount only if depreciation entry is made ([#21894](https://github.com/frappe/erpnext/pull/21894))
|
||||
- Throw error if no serial numbers are found in Pick List ([#21914](https://github.com/frappe/erpnext/pull/21914))
|
||||
- Shopify error message on failure of sales order creation ([#21924](https://github.com/frappe/erpnext/pull/21924))
|
||||
- Don't prompt for Quality Inspection on Return Documents. ([#22200](https://github.com/frappe/erpnext/pull/22200))
|
||||
- Item tax template not getting mapped from source to target doc ([#21863](https://github.com/frappe/erpnext/pull/21863))
|
||||
- Create purchase invoice from purchase receipt dashboard ([#22087](https://github.com/frappe/erpnext/pull/22087))
|
||||
- Procurement Tracker Data Consistency ([#22062](https://github.com/frappe/erpnext/pull/22062))
|
||||
- Cannot assign same task to other asset maintenance ([#22024](https://github.com/frappe/erpnext/pull/22024))
|
||||
- Incorrect VAT rate display in Sales Invoice (UAE) ([#21883](https://github.com/frappe/erpnext/pull/21883))
|
||||
- Missing income account when getting free product ([#22158](https://github.com/frappe/erpnext/pull/22158))
|
||||
- Item tax template not applied if valid from is blank ([#21819](https://github.com/frappe/erpnext/pull/21819))
|
||||
- Tax ID is not fetched when creating Sales Order from Quotation ([#21786](https://github.com/frappe/erpnext/pull/21786))
|
||||
- Disposed asset creates inconsistencies in asset depreciation report ([#22021](https://github.com/frappe/erpnext/pull/22021))
|
||||
- Submitted sales order can be updated with proper permission ([#22218](https://github.com/frappe/erpnext/pull/22218))
|
||||
- Import supplier invoice not working ([#22108](https://github.com/frappe/erpnext/pull/22108))
|
||||
- Showing Wrong balance on allocation boundary dates ([#21908](https://github.com/frappe/erpnext/pull/21908))
|
||||
- Fetch customer into Delivery Note from Pick List ([#21825](https://github.com/frappe/erpnext/pull/21825))
|
||||
- Apply shipping rule without address ([#22093](https://github.com/frappe/erpnext/pull/22093))
|
||||
- Prioritize Default Customer Price List in Portal ([#22183](https://github.com/frappe/erpnext/pull/22183))
|
||||
- Supplier Invoice No not fetched in Import Supplier Invoice ([#21829](https://github.com/frappe/erpnext/pull/21829))
|
||||
- Item Price and Add to Cart not showing on Website ([#21905](https://github.com/frappe/erpnext/pull/21905))
|
||||
- Make transaction date of the oldest transaction as the last integration date ([#22017](https://github.com/frappe/erpnext/pull/22017))
|
||||
- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068))
|
||||
@@ -1,8 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return [
|
||||
config = [
|
||||
{
|
||||
"label": _("Purchasing"),
|
||||
"icon": "fa fa-star",
|
||||
@@ -243,3 +244,21 @@ def get_data():
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
regional = {
|
||||
"label": _("Regional"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Import Supplier Invoice",
|
||||
"description": _("Import Italian Supplier Invoice."),
|
||||
"onboard": 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
countries = frappe.get_all("Company", fields="country")
|
||||
countries = [country["country"] for country in countries]
|
||||
if "Italy" in countries:
|
||||
config.append(regional)
|
||||
return config
|
||||
@@ -1134,8 +1134,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.uom = item.stock_uom
|
||||
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||
if not child_item.warehouse:
|
||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||
@@ -1154,8 +1154,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.uom = item.stock_uom
|
||||
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
return child_item
|
||||
@@ -1178,7 +1178,7 @@ def check_and_delete_children(parent, data):
|
||||
|
||||
if parent.doctype == "Purchase Order" and flt(d.received_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
|
||||
|
||||
|
||||
if flt(d.billed_amt):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
|
||||
|
||||
@@ -1187,6 +1187,26 @@ def check_and_delete_children(parent, data):
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||
def check_permissions(doc, perm_type='create'):
|
||||
try:
|
||||
doc.check_permission(perm_type)
|
||||
except:
|
||||
action = "add" if perm_type == 'create' else "update"
|
||||
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
|
||||
|
||||
def get_new_child_item(item_row):
|
||||
if parent_doctype == "Sales Order":
|
||||
return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
|
||||
if parent_doctype == "Purchase Order":
|
||||
return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
|
||||
|
||||
def validate_quantity(child_item, d):
|
||||
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
||||
frappe.throw(_("Cannot set quantity less than delivered quantity"))
|
||||
|
||||
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
|
||||
frappe.throw(_("Cannot set quantity less than received quantity"))
|
||||
|
||||
data = json.loads(trans_items)
|
||||
|
||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||
@@ -1198,20 +1218,29 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
new_child_flag = False
|
||||
if not d.get("docname"):
|
||||
new_child_flag = True
|
||||
if parent_doctype == "Sales Order":
|
||||
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
|
||||
if parent_doctype == "Purchase Order":
|
||||
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
|
||||
check_permissions(parent, 'create')
|
||||
child_item = get_new_child_item(d)
|
||||
else:
|
||||
check_permissions(parent, 'write')
|
||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||
if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
|
||||
|
||||
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
|
||||
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
|
||||
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
|
||||
|
||||
if parent_doctype == 'Sales Order':
|
||||
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
||||
elif parent_doctype == 'Purchase Order':
|
||||
prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date")
|
||||
|
||||
rate_unchanged = prev_rate == new_rate
|
||||
qty_unchanged = prev_qty == new_qty
|
||||
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
||||
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
|
||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
|
||||
continue
|
||||
|
||||
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
||||
frappe.throw(_("Cannot set quantity less than delivered quantity"))
|
||||
|
||||
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
|
||||
frappe.throw(_("Cannot set quantity less than received quantity"))
|
||||
validate_quantity(child_item, d)
|
||||
|
||||
child_item.qty = flt(d.get("qty"))
|
||||
precision = child_item.precision("rate") or 2
|
||||
@@ -1222,6 +1251,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"))
|
||||
|
||||
if d.get("conversion_factor"):
|
||||
if child_item.stock_uom == child_item.uom:
|
||||
child_item.conversion_factor = 1
|
||||
else:
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor'))
|
||||
|
||||
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
||||
child_item.delivery_date = d.get('delivery_date')
|
||||
|
||||
if d.get("schedule_date") and parent_doctype == 'Purchase Order':
|
||||
child_item.schedule_date = d.get('schedule_date')
|
||||
|
||||
if flt(child_item.price_list_rate):
|
||||
if flt(child_item.rate) > flt(child_item.price_list_rate):
|
||||
# if rate is greater than price_list_rate, set margin
|
||||
|
||||
@@ -340,7 +340,7 @@ class BuyingController(StockController):
|
||||
})
|
||||
|
||||
if not rm.rate:
|
||||
rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse,
|
||||
rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse,
|
||||
self.doctype, self.name, currency=self.company_currency, company=self.company)
|
||||
|
||||
rm.amount = qty * flt(rm.rate)
|
||||
|
||||
@@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None):
|
||||
|
||||
else:
|
||||
attributes_list = attribute_values.get(attribute.lower(), [])
|
||||
validate_item_attribute_value(attributes_list, attribute, value, item.name)
|
||||
validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True)
|
||||
|
||||
def validate_is_incremental(numeric_attribute, attribute, value, item):
|
||||
from_range = numeric_attribute.from_range
|
||||
@@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item):
|
||||
.format(attribute, from_range, to_range, increment, item),
|
||||
InvalidItemAttributeValueError, title=_('Invalid Attribute'))
|
||||
|
||||
def validate_item_attribute_value(attributes_list, attribute, attribute_value, item):
|
||||
def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True):
|
||||
allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value')
|
||||
if allow_rename_attribute_value:
|
||||
pass
|
||||
elif attribute_value not in attributes_list:
|
||||
frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format(
|
||||
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed'))
|
||||
if from_variant:
|
||||
frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
|
||||
frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value"))
|
||||
else:
|
||||
msg = _("The value {0} is already assigned to an existing Item {1}.").format(
|
||||
frappe.bold(attribute_value), frappe.bold(item))
|
||||
msg += "<br>" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value"))
|
||||
|
||||
frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed'))
|
||||
|
||||
def get_attribute_values(item):
|
||||
if not frappe.flags.attribute_values:
|
||||
|
||||
@@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
|
||||
class StockController(AccountsController):
|
||||
def validate(self):
|
||||
super(StockController, self).validate()
|
||||
self.validate_inspection()
|
||||
if not self.get('is_return'):
|
||||
self.validate_inspection()
|
||||
self.validate_serialized_batch()
|
||||
self.validate_customer_provided_item()
|
||||
|
||||
@@ -224,7 +225,9 @@ class StockController(AccountsController):
|
||||
|
||||
def check_expense_account(self, item):
|
||||
if not item.get("expense_account"):
|
||||
frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code))
|
||||
frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \
|
||||
Account in the Items table").format(item.idx, frappe.bold(item.item_code)),
|
||||
title=_("Expense Account Missing"))
|
||||
|
||||
else:
|
||||
is_expense_account = frappe.db.get_value("Account",
|
||||
|
||||
@@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False):
|
||||
return frappe.db.exists(doctype, get_customer_filter(doc, customers))
|
||||
elif suppliers:
|
||||
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
|
||||
return frappe.db.exists(doctype, filters={
|
||||
return frappe.db.exists(doctype, {
|
||||
'name': doc.name,
|
||||
fieldname: ["in", suppliers]
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-20 14:39:33.300588",
|
||||
"modified": "2020-05-20 12:37:17.431879",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Sales Stage",
|
||||
@@ -41,4 +41,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'assessment_plan',
|
||||
'non_standard_fieldnames': {
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Assessment'),
|
||||
'items': ['Assessment Result']
|
||||
}
|
||||
]
|
||||
}
|
||||
25
erpnext/education/doctype/course/course_dashboard.py
Normal file
25
erpnext/education/doctype/course/course_dashboard.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'course',
|
||||
'non_standard_fieldnames': {
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Course'),
|
||||
'items': ['Course Enrollment', 'Course Schedule']
|
||||
},
|
||||
{
|
||||
'label': _('Student'),
|
||||
'items': ['Student Group']
|
||||
},
|
||||
{
|
||||
'label': _('Assessment'),
|
||||
'items': ['Assessment Plan']
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2015-09-07 14:37:01.886859",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@@ -16,26 +17,33 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Course",
|
||||
"options": "Course",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
{
|
||||
"fetch_from": "course.course_name",
|
||||
"fieldname": "course_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Course Name",
|
||||
"fetch_from": "course.course_name",
|
||||
"read_only":1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "required",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Mandatory"
|
||||
"label": "Mandatory",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-06-12 12:42:12.845972",
|
||||
"links": [],
|
||||
"modified": "2020-06-09 18:56:10.213241",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Education",
|
||||
"name": "Program Course",
|
||||
@@ -45,4 +53,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
@@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
|
||||
items = get_order_items(shopify_order.get("line_items"), shopify_settings)
|
||||
|
||||
if not items:
|
||||
message = 'Following items are exists in order but relevant record not found in Product master'
|
||||
message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master'
|
||||
message += "\n" + ", ".join(product_not_exists)
|
||||
|
||||
make_shopify_log(status="Error", exception=e, rollback=True)
|
||||
make_shopify_log(status="Error", exception=message, rollback=True)
|
||||
|
||||
return ''
|
||||
|
||||
@@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings):
|
||||
return taxes
|
||||
|
||||
def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
|
||||
"""Shipping lines represents the shipping details,
|
||||
each such shipping detail consists of a list of tax_lines"""
|
||||
for shipping_charge in shipping_lines:
|
||||
taxes.append({
|
||||
"charge_type": _("Actual"),
|
||||
"account_head": get_tax_account_head(shipping_charge),
|
||||
"description": shipping_charge["title"],
|
||||
"tax_amount": shipping_charge["price"],
|
||||
"cost_center": shopify_settings.cost_center
|
||||
})
|
||||
for tax in shipping_charge.get("tax_lines"):
|
||||
taxes.append({
|
||||
"charge_type": _("Actual"),
|
||||
"account_head": get_tax_account_head(tax),
|
||||
"description": tax["title"],
|
||||
"tax_amount": tax["price"],
|
||||
"cost_center": shopify_settings.cost_center
|
||||
})
|
||||
|
||||
return taxes
|
||||
|
||||
|
||||
@@ -49,12 +49,13 @@ def _order(*args, **kwargs):
|
||||
if event == "created":
|
||||
sys_lang = frappe.get_single("System Settings").language or 'en'
|
||||
raw_billing_data = order.get("billing")
|
||||
raw_shipping_data = order.get("shipping")
|
||||
customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
|
||||
link_customer_and_address(raw_billing_data, customer_name)
|
||||
link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name)
|
||||
link_items(order.get("line_items"), woocommerce_settings, sys_lang)
|
||||
create_sales_order(order, woocommerce_settings, customer_name, sys_lang)
|
||||
|
||||
def link_customer_and_address(raw_billing_data, customer_name):
|
||||
def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name):
|
||||
customer_woo_com_email = raw_billing_data.get("email")
|
||||
customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email})
|
||||
if not customer_exists:
|
||||
@@ -68,38 +69,80 @@ def link_customer_and_address(raw_billing_data, customer_name):
|
||||
customer.customer_name = customer_name
|
||||
customer.woocommerce_email = customer_woo_com_email
|
||||
customer.flags.ignore_mandatory = True
|
||||
customer.save()
|
||||
customer.save()
|
||||
|
||||
if customer_exists:
|
||||
frappe.rename_doc("Customer", old_name, customer_name)
|
||||
address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email})
|
||||
for address_type in ("Billing", "Shipping",):
|
||||
try:
|
||||
address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
|
||||
rename_address(address, customer)
|
||||
except (
|
||||
frappe.DoesNotExistError,
|
||||
frappe.DuplicateEntryError,
|
||||
frappe.ValidationError,
|
||||
):
|
||||
pass
|
||||
else:
|
||||
address = frappe.new_doc("Address")
|
||||
create_address(raw_billing_data, customer, "Billing")
|
||||
create_address(raw_shipping_data, customer, "Shipping")
|
||||
create_contact(raw_billing_data, customer)
|
||||
|
||||
address.address_line1 = raw_billing_data.get("address_1", "Not Provided")
|
||||
address.address_line2 = raw_billing_data.get("address_2", "Not Provided")
|
||||
address.city = raw_billing_data.get("city", "Not Provided")
|
||||
address.woocommerce_email = customer_woo_com_email
|
||||
address.address_type = "Billing"
|
||||
address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()})
|
||||
address.state = raw_billing_data.get("state")
|
||||
address.pincode = raw_billing_data.get("postcode")
|
||||
address.phone = raw_billing_data.get("phone")
|
||||
address.email_id = customer_woo_com_email
|
||||
def create_contact(data, customer):
|
||||
email = data.get("email", None)
|
||||
phone = data.get("phone", None)
|
||||
|
||||
if not email and not phone:
|
||||
return
|
||||
|
||||
contact = frappe.new_doc("Contact")
|
||||
contact.first_name = data.get("first_name")
|
||||
contact.last_name = data.get("last_name")
|
||||
contact.is_primary_contact = 1
|
||||
contact.is_billing_contact = 1
|
||||
|
||||
if phone:
|
||||
contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1)
|
||||
|
||||
if email:
|
||||
contact.add_email(email, is_primary=1)
|
||||
|
||||
contact.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": customer.name
|
||||
})
|
||||
|
||||
contact.flags.ignore_mandatory = True
|
||||
contact.save()
|
||||
|
||||
def create_address(raw_data, customer, address_type):
|
||||
address = frappe.new_doc("Address")
|
||||
|
||||
address.address_line1 = raw_data.get("address_1", "Not Provided")
|
||||
address.address_line2 = raw_data.get("address_2", "Not Provided")
|
||||
address.city = raw_data.get("city", "Not Provided")
|
||||
address.woocommerce_email = customer.woocommerce_email
|
||||
address.address_type = address_type
|
||||
address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()})
|
||||
address.state = raw_data.get("state")
|
||||
address.pincode = raw_data.get("postcode")
|
||||
address.phone = raw_data.get("phone")
|
||||
address.email_id = customer.woocommerce_email
|
||||
address.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": customer.customer_name
|
||||
"link_name": customer.name
|
||||
})
|
||||
|
||||
address.flags.ignore_mandatory = True
|
||||
address = address.save()
|
||||
address.save()
|
||||
|
||||
if customer_exists:
|
||||
old_address_title = address.name
|
||||
new_address_title = customer.customer_name + "-billing"
|
||||
address.address_title = customer.customer_name
|
||||
address.save()
|
||||
def rename_address(address, customer):
|
||||
old_address_title = address.name
|
||||
new_address_title = customer.name + "-" + address.address_type
|
||||
address.address_title = customer.customer_name
|
||||
address.save()
|
||||
|
||||
frappe.rename_doc("Address", old_address_title, new_address_title)
|
||||
frappe.rename_doc("Address", old_address_title, new_address_title)
|
||||
|
||||
def link_items(items_list, woocommerce_settings, sys_lang):
|
||||
for item_data in items_list:
|
||||
@@ -111,7 +154,7 @@ def link_items(items_list, woocommerce_settings, sys_lang):
|
||||
else:
|
||||
#Create Item
|
||||
item = frappe.new_doc("Item")
|
||||
|
||||
|
||||
item.item_name = item_data.get("name")
|
||||
item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id"))
|
||||
item.woocommerce_id = item_data.get("product_id")
|
||||
@@ -145,7 +188,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l
|
||||
company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
|
||||
|
||||
default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr)
|
||||
if not frappe.db.exists("Warehouse", default_warehouse):
|
||||
if not frappe.db.exists("Warehouse", default_warehouse) \
|
||||
and not woocommerce_settings.warehouse:
|
||||
frappe.throw(_("Please set Warehouse in Woocommerce Settings"))
|
||||
|
||||
for item in order.get("line_items"):
|
||||
@@ -171,7 +215,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l
|
||||
|
||||
add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account)
|
||||
add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account)
|
||||
|
||||
|
||||
def add_tax_details(sales_order, price, desc, tax_account_head):
|
||||
sales_order.append("taxes", {
|
||||
"charge_type":"Actual",
|
||||
|
||||
@@ -124,10 +124,12 @@ def add_account_subtype(account_subtype):
|
||||
|
||||
@frappe.whitelist()
|
||||
def sync_transactions(bank, bank_account):
|
||||
''' Sync transactions based on the last integration date as the start date, after sync is completed
|
||||
add the transaction date of the oldest transaction as the last integration date '''
|
||||
|
||||
last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
||||
if last_sync_date:
|
||||
start_date = formatdate(last_sync_date, "YYYY-MM-dd")
|
||||
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
||||
if last_transaction_date:
|
||||
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
|
||||
else:
|
||||
start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd")
|
||||
end_date = formatdate(today(), "YYYY-MM-dd")
|
||||
@@ -139,12 +141,14 @@ def sync_transactions(bank, bank_account):
|
||||
for transaction in reversed(transactions):
|
||||
result += new_bank_transaction(transaction)
|
||||
|
||||
frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
|
||||
len(result), bank_account, start_date, end_date))
|
||||
if result:
|
||||
last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date')
|
||||
|
||||
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date))
|
||||
frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
|
||||
len(result), bank_account, start_date, end_date))
|
||||
|
||||
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
|
||||
|
||||
return result
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"label": "Customer Settings"
|
||||
},
|
||||
{
|
||||
"description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order",
|
||||
"description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order",
|
||||
"fieldname": "default_customer",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Customer",
|
||||
@@ -258,7 +258,7 @@
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"modified": "2019-09-13 12:32:11.384757",
|
||||
"modified": "2020-05-28 12:32:11.384757",
|
||||
"modified_by": "umair@erpnext.com",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Shopify Settings",
|
||||
@@ -277,4 +277,4 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import get_request_session
|
||||
from requests.exceptions import HTTPError
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from erpnext.erpnext_integrations.utils import get_webhook_address
|
||||
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log
|
||||
@@ -29,19 +30,24 @@ class ShopifySettings(Document):
|
||||
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
|
||||
# url = get_shopify_url('admin/webhooks.json', self)
|
||||
created_webhooks = [d.method for d in self.webhooks]
|
||||
url = get_shopify_url('admin/api/2019-04/webhooks.json', self)
|
||||
url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
|
||||
for method in webhooks:
|
||||
session = get_request_session()
|
||||
try:
|
||||
d = session.post(url, data=json.dumps({
|
||||
res = session.post(url, data=json.dumps({
|
||||
"webhook": {
|
||||
"topic": method,
|
||||
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
|
||||
"format": "json"
|
||||
}
|
||||
}), headers=get_header(self))
|
||||
d.raise_for_status()
|
||||
self.update_webhook_table(method, d.json())
|
||||
res.raise_for_status()
|
||||
self.update_webhook_table(method, res.json())
|
||||
|
||||
except HTTPError as e:
|
||||
error_message = res.json().get('errors', e)
|
||||
make_shopify_log(status="Warning", exception=error_message, rollback=True)
|
||||
|
||||
except Exception as e:
|
||||
make_shopify_log(status="Warning", exception=e, rollback=True)
|
||||
|
||||
@@ -50,13 +56,18 @@ class ShopifySettings(Document):
|
||||
deleted_webhooks = []
|
||||
|
||||
for d in self.webhooks:
|
||||
url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self)
|
||||
url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
|
||||
try:
|
||||
res = session.delete(url, headers=get_header(self))
|
||||
res.raise_for_status()
|
||||
deleted_webhooks.append(d)
|
||||
|
||||
except HTTPError as e:
|
||||
error_message = res.json().get('errors', e)
|
||||
make_shopify_log(status="Warning", exception=error_message, rollback=True)
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(message=frappe.get_traceback(), title=e)
|
||||
frappe.log_error(message=e, title='Shopify Webhooks Issue')
|
||||
|
||||
for d in deleted_webhooks:
|
||||
self.remove(d)
|
||||
@@ -125,4 +136,3 @@ def setup_custom_fields():
|
||||
}
|
||||
|
||||
create_custom_fields(custom_fields)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
|
||||
shopify_variants_attr_list = ["option1", "option2", "option3"]
|
||||
|
||||
def sync_item_from_shopify(shopify_settings, item):
|
||||
url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
|
||||
url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
|
||||
session = get_request_session()
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Tally Migration', {
|
||||
frappe.provide("erpnext.tally_migration");
|
||||
|
||||
frappe.ui.form.on("Tally Migration", {
|
||||
onload: function (frm) {
|
||||
let reload_status = true;
|
||||
frappe.realtime.on("tally_migration_progress_update", function (data) {
|
||||
@@ -35,7 +37,17 @@ frappe.ui.form.on('Tally Migration', {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.trigger("show_logs_preview");
|
||||
erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log);
|
||||
erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log);
|
||||
|
||||
["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => {
|
||||
frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1)
|
||||
frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1)
|
||||
})
|
||||
|
||||
if (frm.doc.master_data && !frm.doc.is_master_data_imported) {
|
||||
if (frm.doc.is_master_data_processed) {
|
||||
if (frm.doc.status != "Importing Master Data") {
|
||||
@@ -47,6 +59,7 @@ frappe.ui.form.on('Tally Migration', {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) {
|
||||
if (frm.doc.is_day_book_data_processed) {
|
||||
if (frm.doc.status != "Importing Day Book Data") {
|
||||
@@ -59,6 +72,17 @@ frappe.ui.form.on('Tally Migration', {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
erpnext_company: function (frm) {
|
||||
frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => {
|
||||
if (exists) {
|
||||
frappe.msgprint(
|
||||
__("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
add_button: function (frm, label, method) {
|
||||
frm.add_custom_button(
|
||||
label,
|
||||
@@ -71,5 +95,255 @@ frappe.ui.form.on('Tally Migration', {
|
||||
frm.reload_doc();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
render_html_table(frm, shown_logs, hidden_logs, field) {
|
||||
if (shown_logs && shown_logs.length > 0) {
|
||||
frm.toggle_display(field, true);
|
||||
} else {
|
||||
frm.toggle_display(field, false);
|
||||
return
|
||||
}
|
||||
let rows = erpnext.tally_migration.get_html_rows(shown_logs, field);
|
||||
let rows_head, table_caption;
|
||||
|
||||
let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? `<tr class="text-muted">
|
||||
<td colspan="4">And ${hidden_logs.length} more others</td>
|
||||
</tr>`: "";
|
||||
|
||||
if (field === "fixed_error_log_preview") {
|
||||
rows_head = `<th width="75%">${__("Meta Data")}</th>
|
||||
<th width="10%">${__("Unresolve")}</th>`
|
||||
table_caption = "Resolved Issues"
|
||||
} else {
|
||||
rows_head = `<th width="75%">${__("Error Message")}</th>
|
||||
<th width="10%">${__("Create")}</th>`
|
||||
table_caption = "Error Log"
|
||||
}
|
||||
|
||||
frm.get_field(field).$wrapper.html(`
|
||||
<table class="table table-bordered">
|
||||
<caption>${table_caption}</caption>
|
||||
<tr class="text-muted">
|
||||
<th width="5%">${__("#")}</th>
|
||||
<th width="10%">${__("DocType")}</th>
|
||||
${rows_head}
|
||||
</tr>
|
||||
${rows}
|
||||
${table_footer}
|
||||
</table>
|
||||
`);
|
||||
},
|
||||
|
||||
show_error_summary(frm) {
|
||||
let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => {
|
||||
if (row.doc) {
|
||||
if (summary[row.doc.doctype]) {
|
||||
summary[row.doc.doctype] += 1;
|
||||
} else {
|
||||
summary[row.doc.doctype] = 1;
|
||||
}
|
||||
}
|
||||
return summary
|
||||
}, {});
|
||||
console.table(summary);
|
||||
},
|
||||
|
||||
show_logs_preview(frm) {
|
||||
let empty = "[]";
|
||||
let import_log = frm.doc.failed_import_log || empty;
|
||||
let completed_log = frm.doc.fixed_errors_log || empty;
|
||||
let render_section = !(import_log === completed_log && import_log === empty);
|
||||
|
||||
frm.toggle_display("import_log_section", render_section);
|
||||
if (render_section) {
|
||||
frm.trigger("show_error_summary");
|
||||
frm.trigger("show_errored_import_log");
|
||||
frm.trigger("show_fixed_errors_log");
|
||||
}
|
||||
},
|
||||
|
||||
show_errored_import_log(frm) {
|
||||
let import_log = erpnext.tally_migration.failed_import_log;
|
||||
let logs = import_log.slice(0, 20);
|
||||
let hidden_logs = import_log.slice(20);
|
||||
|
||||
frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview");
|
||||
},
|
||||
|
||||
show_fixed_errors_log(frm) {
|
||||
let completed_log = erpnext.tally_migration.fixed_errors_log;
|
||||
let logs = completed_log.slice(0, 20);
|
||||
let hidden_logs = completed_log.slice(20);
|
||||
|
||||
frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview");
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.tally_migration.getError = (traceback) => {
|
||||
/* Extracts the Error Message from the Python Traceback or Solved error */
|
||||
let is_multiline = traceback.trim().indexOf("\n") != -1;
|
||||
let message;
|
||||
|
||||
if (is_multiline) {
|
||||
let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1
|
||||
let error_line = traceback.substr(exc_error_idx)
|
||||
let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0;
|
||||
message = error_line.slice(split_str_idx).trim();
|
||||
} else {
|
||||
message = traceback;
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
erpnext.tally_migration.cleanDoc = (obj) => {
|
||||
/* Strips all null and empty values of your JSON object */
|
||||
let temp = obj;
|
||||
$.each(temp, function(key, value){
|
||||
if (value === "" || value === null){
|
||||
delete obj[key];
|
||||
} else if (Object.prototype.toString.call(value) === '[object Object]') {
|
||||
erpnext.tally_migration.cleanDoc(value);
|
||||
} else if ($.isArray(value)) {
|
||||
$.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); });
|
||||
}
|
||||
});
|
||||
return temp;
|
||||
}
|
||||
|
||||
erpnext.tally_migration.unresolve = (document) => {
|
||||
/* Mark document migration as unresolved ie. move to failed error log */
|
||||
let frm = cur_frm;
|
||||
let failed_log = erpnext.tally_migration.failed_import_log;
|
||||
let fixed_log = erpnext.tally_migration.fixed_errors_log;
|
||||
|
||||
let modified_fixed_log = fixed_log.filter(row => {
|
||||
if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) {
|
||||
return row
|
||||
}
|
||||
});
|
||||
|
||||
failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` });
|
||||
|
||||
frm.doc.failed_import_log = JSON.stringify(failed_log);
|
||||
frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log);
|
||||
|
||||
frm.dirty();
|
||||
frm.save();
|
||||
}
|
||||
|
||||
erpnext.tally_migration.resolve = (document) => {
|
||||
/* Mark document migration as resolved ie. move to fixed error log */
|
||||
let frm = cur_frm;
|
||||
let failed_log = erpnext.tally_migration.failed_import_log;
|
||||
let fixed_log = erpnext.tally_migration.fixed_errors_log;
|
||||
|
||||
let modified_failed_log = failed_log.filter(row => {
|
||||
if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) {
|
||||
return row
|
||||
}
|
||||
});
|
||||
fixed_log.push({ doc: document, exc: `Solved on ${Date()}` });
|
||||
|
||||
frm.doc.failed_import_log = JSON.stringify(modified_failed_log);
|
||||
frm.doc.fixed_errors_log = JSON.stringify(fixed_log);
|
||||
|
||||
frm.dirty();
|
||||
frm.save();
|
||||
}
|
||||
|
||||
erpnext.tally_migration.create_new_doc = (document) => {
|
||||
/* Mark as resolved and create new document */
|
||||
erpnext.tally_migration.resolve(document);
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc',
|
||||
args: {
|
||||
document
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
frappe.model.sync(r.message);
|
||||
frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true;
|
||||
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
erpnext.tally_migration.get_html_rows = (logs, field) => {
|
||||
let index = 0;
|
||||
let rows = logs
|
||||
.map(({ doc, exc }) => {
|
||||
let id = frappe.dom.get_unique_id();
|
||||
let traceback = exc;
|
||||
|
||||
let error_message = erpnext.tally_migration.getError(traceback);
|
||||
index++;
|
||||
|
||||
let show_traceback = `
|
||||
<button class="btn btn-default btn-xs m-3" type="button" data-toggle="collapse" data-target="#${id}-traceback" aria-expanded="false" aria-controls="${id}-traceback">
|
||||
${__("Show Traceback")}
|
||||
</button>
|
||||
<div class="collapse margin-top" id="${id}-traceback">
|
||||
<div class="well">
|
||||
<pre style="font-size: smaller;">${traceback}</pre>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
let show_doc = `
|
||||
<button class='btn btn-default btn-xs m-3' type='button' data-toggle='collapse' data-target='#${id}-doc' aria-expanded='false' aria-controls='${id}-doc'>
|
||||
${__("Show Document")}
|
||||
</button>
|
||||
<div class="collapse margin-top" id="${id}-doc">
|
||||
<div class="well">
|
||||
<pre style="font-size: smaller;">${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}</pre>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
let create_button = `
|
||||
<button class='btn btn-default btn-xs m-3' type='button' onclick='erpnext.tally_migration.create_new_doc(${JSON.stringify(doc)})'>
|
||||
${__("Create Document")}
|
||||
</button>`
|
||||
|
||||
let mark_as_unresolved = `
|
||||
<button class='btn btn-default btn-xs m-3' type='button' onclick='erpnext.tally_migration.unresolve(${JSON.stringify(doc)})'>
|
||||
${__("Mark as unresolved")}
|
||||
</button>`
|
||||
|
||||
if (field === "fixed_error_log_preview") {
|
||||
return `<tr>
|
||||
<td>${index}</td>
|
||||
<td>
|
||||
<div>${doc.doctype}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>${error_message}</div>
|
||||
<div>${show_doc}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>${mark_as_unresolved}</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
} else {
|
||||
return `<tr>
|
||||
<td>${index}</td>
|
||||
<td>
|
||||
<div>${doc.doctype}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>${error_message}</div>
|
||||
<div>${show_traceback}</div>
|
||||
<div>${show_doc}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>${create_button}</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
}).join("");
|
||||
|
||||
return rows
|
||||
}
|
||||
@@ -28,14 +28,19 @@
|
||||
"vouchers",
|
||||
"accounts_section",
|
||||
"default_warehouse",
|
||||
"round_off_account",
|
||||
"default_round_off_account",
|
||||
"column_break_21",
|
||||
"default_cost_center",
|
||||
"day_book_section",
|
||||
"day_book_data",
|
||||
"column_break_27",
|
||||
"is_day_book_data_processed",
|
||||
"is_day_book_data_imported"
|
||||
"is_day_book_data_imported",
|
||||
"import_log_section",
|
||||
"failed_import_log",
|
||||
"fixed_errors_log",
|
||||
"failed_import_preview",
|
||||
"fixed_error_log_preview"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -57,6 +62,7 @@
|
||||
"fieldname": "tally_creditors_account",
|
||||
"fieldtype": "Data",
|
||||
"label": "Tally Creditors Account",
|
||||
"read_only_depends_on": "eval:doc.is_master_data_processed==1",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -69,6 +75,7 @@
|
||||
"fieldname": "tally_debtors_account",
|
||||
"fieldtype": "Data",
|
||||
"label": "Tally Debtors Account",
|
||||
"read_only_depends_on": "eval:doc.is_master_data_processed==1",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -92,7 +99,7 @@
|
||||
"fieldname": "erpnext_company",
|
||||
"fieldtype": "Data",
|
||||
"label": "ERPNext Company",
|
||||
"read_only_depends_on": "eval:doc.is_master_data_processed == 1"
|
||||
"read_only_depends_on": "eval:doc.is_master_data_processed==1"
|
||||
},
|
||||
{
|
||||
"fieldname": "processed_files_section",
|
||||
@@ -136,6 +143,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "is_master_data_imported",
|
||||
"description": "The accounts are set by the system automatically but do confirm these defaults",
|
||||
"fieldname": "accounts_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounts"
|
||||
@@ -146,12 +154,6 @@
|
||||
"label": "Default Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "round_off_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Round Off Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
@@ -212,11 +214,47 @@
|
||||
"fieldname": "default_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default UOM",
|
||||
"options": "UOM"
|
||||
"options": "UOM",
|
||||
"read_only_depends_on": "eval:doc.is_master_data_imported==1"
|
||||
},
|
||||
{
|
||||
"default": "[]",
|
||||
"fieldname": "failed_import_log",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "failed_import_preview",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Failed Import Log"
|
||||
},
|
||||
{
|
||||
"fieldname": "import_log_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Import Log"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_round_off_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Round Off Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"default": "[]",
|
||||
"fieldname": "fixed_errors_log",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "fixed_error_log_preview",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Fixed Error Log"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-16 13:03:28.894919",
|
||||
"modified": "2020-04-28 00:29:18.039826",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Tally Migration",
|
||||
|
||||
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import zipfile
|
||||
from decimal import Decimal
|
||||
@@ -15,18 +16,34 @@ from bs4 import BeautifulSoup as bs
|
||||
import frappe
|
||||
from erpnext import encode_company_abbr
|
||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
|
||||
from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data
|
||||
|
||||
from frappe import _
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import getseries, revert_series_if_last
|
||||
from frappe.utils.data import format_datetime
|
||||
|
||||
|
||||
PRIMARY_ACCOUNT = "Primary"
|
||||
VOUCHER_CHUNK_SIZE = 500
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def new_doc(document):
|
||||
document = json.loads(document)
|
||||
doctype = document.pop("doctype")
|
||||
document.pop("name", None)
|
||||
doc = frappe.new_doc(doctype)
|
||||
doc.update(document)
|
||||
|
||||
return doc
|
||||
|
||||
class TallyMigration(Document):
|
||||
def validate(self):
|
||||
failed_import_log = json.loads(self.failed_import_log)
|
||||
sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"])
|
||||
self.failed_import_log = json.dumps(sorted_failed_import_log)
|
||||
|
||||
def autoname(self):
|
||||
if not self.name:
|
||||
self.name = "Tally Migration on " + format_datetime(self.creation)
|
||||
@@ -65,9 +82,17 @@ class TallyMigration(Document):
|
||||
"attached_to_name": self.name,
|
||||
"content": json.dumps(value),
|
||||
"is_private": True
|
||||
}).insert()
|
||||
})
|
||||
try:
|
||||
f.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
setattr(self, key, f.file_url)
|
||||
|
||||
def set_account_defaults(self):
|
||||
self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"])
|
||||
self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse")
|
||||
|
||||
def _process_master_data(self):
|
||||
def get_company_name(collection):
|
||||
return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip()
|
||||
@@ -84,7 +109,11 @@ class TallyMigration(Document):
|
||||
children, parents = get_children_and_parent_dict(accounts)
|
||||
group_set = [acc[1] for acc in accounts if acc[2]]
|
||||
children, customers, suppliers = remove_parties(parents, children, group_set)
|
||||
coa = traverse({}, children, roots, roots, group_set)
|
||||
|
||||
try:
|
||||
coa = traverse({}, children, roots, roots, group_set)
|
||||
except RecursionError:
|
||||
self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name"))
|
||||
|
||||
for account in coa:
|
||||
coa[account]["root_type"] = root_type_map[account]
|
||||
@@ -126,14 +155,18 @@ class TallyMigration(Document):
|
||||
def remove_parties(parents, children, group_set):
|
||||
customers, suppliers = set(), set()
|
||||
for account in parents:
|
||||
found = False
|
||||
if self.tally_creditors_account in parents[account]:
|
||||
children.pop(account, None)
|
||||
found = True
|
||||
if account not in group_set:
|
||||
suppliers.add(account)
|
||||
elif self.tally_debtors_account in parents[account]:
|
||||
children.pop(account, None)
|
||||
if self.tally_debtors_account in parents[account]:
|
||||
found = True
|
||||
if account not in group_set:
|
||||
customers.add(account)
|
||||
if found:
|
||||
children.pop(account, None)
|
||||
|
||||
return children, customers, suppliers
|
||||
|
||||
def traverse(tree, children, accounts, roots, group_set):
|
||||
@@ -151,6 +184,7 @@ class TallyMigration(Document):
|
||||
parties, addresses = [], []
|
||||
for account in collection.find_all("LEDGER"):
|
||||
party_type = None
|
||||
links = []
|
||||
if account.NAME.string.strip() in customers:
|
||||
party_type = "Customer"
|
||||
parties.append({
|
||||
@@ -161,7 +195,9 @@ class TallyMigration(Document):
|
||||
"territory": "All Territories",
|
||||
"customer_type": "Individual",
|
||||
})
|
||||
elif account.NAME.string.strip() in suppliers:
|
||||
links.append({"link_doctype": party_type, "link_name": account["NAME"]})
|
||||
|
||||
if account.NAME.string.strip() in suppliers:
|
||||
party_type = "Supplier"
|
||||
parties.append({
|
||||
"doctype": party_type,
|
||||
@@ -170,6 +206,8 @@ class TallyMigration(Document):
|
||||
"supplier_group": "All Supplier Groups",
|
||||
"supplier_type": "Individual",
|
||||
})
|
||||
links.append({"link_doctype": party_type, "link_name": account["NAME"]})
|
||||
|
||||
if party_type:
|
||||
address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")])
|
||||
addresses.append({
|
||||
@@ -183,7 +221,7 @@ class TallyMigration(Document):
|
||||
"mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
|
||||
"phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
|
||||
"gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
|
||||
"links": [{"link_doctype": party_type, "link_name": account["NAME"]}],
|
||||
"links": links
|
||||
})
|
||||
return parties, addresses
|
||||
|
||||
@@ -242,12 +280,18 @@ class TallyMigration(Document):
|
||||
def create_company_and_coa(coa_file_url):
|
||||
coa_file = frappe.get_doc("File", {"file_url": coa_file_url})
|
||||
frappe.local.flags.ignore_chart_of_accounts = True
|
||||
company = frappe.get_doc({
|
||||
"doctype": "Company",
|
||||
"company_name": self.erpnext_company,
|
||||
"default_currency": "INR",
|
||||
"enable_perpetual_inventory": 0,
|
||||
}).insert()
|
||||
|
||||
try:
|
||||
company = frappe.get_doc({
|
||||
"doctype": "Company",
|
||||
"company_name": self.erpnext_company,
|
||||
"default_currency": "INR",
|
||||
"enable_perpetual_inventory": 0,
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
company = frappe.get_doc("Company", self.erpnext_company)
|
||||
unset_existing_data(self.erpnext_company)
|
||||
|
||||
frappe.local.flags.ignore_chart_of_accounts = False
|
||||
create_charts(company.name, custom_chart=json.loads(coa_file.get_content()))
|
||||
company.create_default_warehouses()
|
||||
@@ -256,36 +300,35 @@ class TallyMigration(Document):
|
||||
parties_file = frappe.get_doc("File", {"file_url": parties_file_url})
|
||||
for party in json.loads(parties_file.get_content()):
|
||||
try:
|
||||
frappe.get_doc(party).insert()
|
||||
party_doc = frappe.get_doc(party)
|
||||
party_doc.insert()
|
||||
except:
|
||||
self.log(party)
|
||||
self.log(party_doc)
|
||||
addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url})
|
||||
for address in json.loads(addresses_file.get_content()):
|
||||
try:
|
||||
frappe.get_doc(address).insert(ignore_mandatory=True)
|
||||
address_doc = frappe.get_doc(address)
|
||||
address_doc.insert(ignore_mandatory=True)
|
||||
except:
|
||||
try:
|
||||
gstin = address.pop("gstin", None)
|
||||
frappe.get_doc(address).insert(ignore_mandatory=True)
|
||||
self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)})
|
||||
except:
|
||||
self.log(address)
|
||||
self.log(address_doc)
|
||||
|
||||
def create_items_uoms(items_file_url, uoms_file_url):
|
||||
uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url})
|
||||
for uom in json.loads(uoms_file.get_content()):
|
||||
if not frappe.db.exists(uom):
|
||||
try:
|
||||
frappe.get_doc(uom).insert()
|
||||
uom_doc = frappe.get_doc(uom)
|
||||
uom_doc.insert()
|
||||
except:
|
||||
self.log(uom)
|
||||
self.log(uom_doc)
|
||||
|
||||
items_file = frappe.get_doc("File", {"file_url": items_file_url})
|
||||
for item in json.loads(items_file.get_content()):
|
||||
try:
|
||||
frappe.get_doc(item).insert()
|
||||
item_doc = frappe.get_doc(item)
|
||||
item_doc.insert()
|
||||
except:
|
||||
self.log(item)
|
||||
self.log(item_doc)
|
||||
|
||||
try:
|
||||
self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4)
|
||||
@@ -299,10 +342,13 @@ class TallyMigration(Document):
|
||||
|
||||
self.publish("Import Master Data", _("Done"), 4, 4)
|
||||
|
||||
self.set_account_defaults()
|
||||
self.is_master_data_imported = 1
|
||||
frappe.db.commit()
|
||||
|
||||
except:
|
||||
self.publish("Import Master Data", _("Process Failed"), -1, 5)
|
||||
frappe.db.rollback()
|
||||
self.log()
|
||||
|
||||
finally:
|
||||
@@ -323,7 +369,9 @@ class TallyMigration(Document):
|
||||
processed_voucher = function(voucher)
|
||||
if processed_voucher:
|
||||
vouchers.append(processed_voucher)
|
||||
frappe.db.commit()
|
||||
except:
|
||||
frappe.db.rollback()
|
||||
self.log(voucher)
|
||||
return vouchers
|
||||
|
||||
@@ -349,6 +397,7 @@ class TallyMigration(Document):
|
||||
journal_entry = {
|
||||
"doctype": "Journal Entry",
|
||||
"tally_guid": voucher.GUID.string.strip(),
|
||||
"tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "",
|
||||
"posting_date": voucher.DATE.string.strip(),
|
||||
"company": self.erpnext_company,
|
||||
"accounts": accounts,
|
||||
@@ -377,6 +426,7 @@ class TallyMigration(Document):
|
||||
"doctype": doctype,
|
||||
party_field: voucher.PARTYNAME.string.strip(),
|
||||
"tally_guid": voucher.GUID.string.strip(),
|
||||
"tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "",
|
||||
"posting_date": voucher.DATE.string.strip(),
|
||||
"due_date": voucher.DATE.string.strip(),
|
||||
"items": get_voucher_items(voucher, doctype),
|
||||
@@ -468,14 +518,21 @@ class TallyMigration(Document):
|
||||
oldest_year = new_year
|
||||
|
||||
def create_custom_fields(doctypes):
|
||||
for doctype in doctypes:
|
||||
df = {
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "tally_guid",
|
||||
"read_only": 1,
|
||||
"label": "Tally GUID"
|
||||
}
|
||||
create_custom_field(doctype, df)
|
||||
tally_guid_df = {
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "tally_guid",
|
||||
"read_only": 1,
|
||||
"label": "Tally GUID"
|
||||
}
|
||||
tally_voucher_no_df = {
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "tally_voucher_no",
|
||||
"read_only": 1,
|
||||
"label": "Tally Voucher Number"
|
||||
}
|
||||
for df in [tally_guid_df, tally_voucher_no_df]:
|
||||
for doctype in doctypes:
|
||||
create_custom_field(doctype, df)
|
||||
|
||||
def create_price_list():
|
||||
frappe.get_doc({
|
||||
@@ -490,7 +547,7 @@ class TallyMigration(Document):
|
||||
try:
|
||||
frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
|
||||
frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
|
||||
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account)
|
||||
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account)
|
||||
|
||||
vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
|
||||
vouchers = json.loads(vouchers_file.get_content())
|
||||
@@ -521,11 +578,14 @@ class TallyMigration(Document):
|
||||
|
||||
for index, voucher in enumerate(chunk, start=start):
|
||||
try:
|
||||
doc = frappe.get_doc(voucher).insert()
|
||||
doc.submit()
|
||||
voucher_doc = frappe.get_doc(voucher)
|
||||
voucher_doc.insert()
|
||||
voucher_doc.submit()
|
||||
self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total)
|
||||
frappe.db.commit()
|
||||
except:
|
||||
self.log(voucher)
|
||||
frappe.db.rollback()
|
||||
self.log(voucher_doc)
|
||||
|
||||
if is_last:
|
||||
self.status = ""
|
||||
@@ -551,9 +611,22 @@ class TallyMigration(Document):
|
||||
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
||||
|
||||
def log(self, data=None):
|
||||
data = data or self.status
|
||||
message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
|
||||
return frappe.log_error(title="Tally Migration Error", message=message)
|
||||
if isinstance(data, frappe.model.document.Document):
|
||||
if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError:
|
||||
failed_import_log = json.loads(self.failed_import_log)
|
||||
doc = data.as_dict()
|
||||
failed_import_log.append({
|
||||
"doc": doc,
|
||||
"exc": traceback.format_exc()
|
||||
})
|
||||
self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':'))
|
||||
self.save()
|
||||
frappe.db.commit()
|
||||
|
||||
else:
|
||||
data = data or self.status
|
||||
message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
|
||||
return frappe.log_error(title="Tally Migration Error", message=message)
|
||||
|
||||
def set_status(self, status=""):
|
||||
self.status = status
|
||||
|
||||
@@ -113,13 +113,13 @@ frappe.ui.form.on('Employee Advance', {
|
||||
employee: function (frm) {
|
||||
if (frm.doc.employee) {
|
||||
return frappe.call({
|
||||
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount",
|
||||
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
|
||||
args: {
|
||||
"employee": frm.doc.employee,
|
||||
"posting_date": frm.doc.posting_date
|
||||
},
|
||||
callback: function(r) {
|
||||
frm.set_value("due_advance_amount",r.message);
|
||||
frm.set_value("pending_amount",r.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"column_break_11",
|
||||
"advance_amount",
|
||||
"paid_amount",
|
||||
"due_advance_amount",
|
||||
"pending_amount",
|
||||
"claimed_amount",
|
||||
"return_amount",
|
||||
"section_break_7",
|
||||
@@ -101,14 +101,6 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:cur_frm.doc.employee",
|
||||
"fieldname": "due_advance_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Due Advance Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "claimed_amount",
|
||||
"fieldtype": "Currency",
|
||||
@@ -169,11 +161,25 @@
|
||||
"label": "Returned Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "repay_unclaimed_amount_from_salary",
|
||||
"fieldtype": "Check",
|
||||
"label": "Repay unclaimed amount from salary"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:cur_frm.doc.employee",
|
||||
"fieldname": "pending_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Pending Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-15 19:04:07.044505",
|
||||
"modified": "2020-06-12 12:42:39.833818",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Advance",
|
||||
|
||||
@@ -95,7 +95,7 @@ class EmployeeAdvance(Document):
|
||||
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_due_advance_amount(employee, posting_date):
|
||||
def get_pending_amount(employee, posting_date):
|
||||
employee_due_amount = frappe.get_all("Employee Advance", \
|
||||
filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \
|
||||
fields = ["advance_amount", "paid_amount"])
|
||||
|
||||
@@ -154,6 +154,14 @@ frappe.ui.form.on("Expense Claim", {
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("cost_center", "expenses", function() {
|
||||
return {
|
||||
filters: {
|
||||
"company": frm.doc.company,
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("account_head", "taxes", function(doc) {
|
||||
return {
|
||||
filters: [
|
||||
|
||||
@@ -428,14 +428,23 @@ def get_leave_details(employee, date):
|
||||
leave_allocation = {}
|
||||
for d in allocation_records:
|
||||
allocation = allocation_records.get(d, frappe._dict())
|
||||
|
||||
total_allocated_leaves = frappe.db.get_value('Leave Allocation', {
|
||||
'from_date': ('<=', date),
|
||||
'to_date': ('>=', date),
|
||||
'leave_type': allocation.leave_type,
|
||||
}, 'SUM(total_leaves_allocated)') or 0
|
||||
|
||||
remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date,
|
||||
consider_all_leaves_in_the_allocation_period=True)
|
||||
|
||||
end_date = allocation.to_date
|
||||
leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1
|
||||
leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date)
|
||||
|
||||
leave_allocation[d] = {
|
||||
"total_leaves": allocation.total_leaves_allocated,
|
||||
"total_leaves": total_allocated_leaves,
|
||||
"expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken),
|
||||
"leaves_taken": leaves_taken,
|
||||
"pending_leaves": leaves_pending,
|
||||
"remaining_leaves": remaining_leaves}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
<table class="table table-bordered small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20%">{{ __("Leave Type") }}</th>
|
||||
<th style="width: 20%" class="text-right">{{ __("Total Allocated Leaves") }}</th>
|
||||
<th style="width: 20%" class="text-right">{{ __("Used Leaves") }}</th>
|
||||
<th style="width: 20%" class="text-right">{{ __("Pending Leaves") }}</th>
|
||||
<th style="width: 20%" class="text-right">{{ __("Available Leaves") }}</th>
|
||||
<th style="width: 16%">{{ __("Leave Type") }}</th>
|
||||
<th style="width: 16%" class="text-right">{{ __("Total Allocated Leaves") }}</th>
|
||||
<th style="width: 16%" class="text-right">{{ __("Expired Leaves") }}</th>
|
||||
<th style="width: 16%" class="text-right">{{ __("Used Leaves") }}</th>
|
||||
<th style="width: 16%" class="text-right">{{ __("Pending Leaves") }}</th>
|
||||
<th style="width: 16%" class="text-right">{{ __("Available Leaves") }}</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
@@ -17,6 +18,7 @@
|
||||
<tr>
|
||||
<td> {%= key %} </td>
|
||||
<td class="text-right"> {%= value["total_leaves"] %} </td>
|
||||
<td class="text-right"> {%= value["expired_leaves"] %} </td>
|
||||
<td class="text-right"> {%= value["leaves_taken"] %} </td>
|
||||
<td class="text-right"> {%= value["pending_leaves"] %} </td>
|
||||
<td class="text-right"> {%= value["remaining_leaves"] %} </td>
|
||||
|
||||
@@ -20,6 +20,9 @@ def get_template():
|
||||
|
||||
args = frappe.local.form_dict
|
||||
|
||||
if getdate(args.from_date) > getdate(args.to_date):
|
||||
frappe.throw(_("To Date should be greater than From Date"))
|
||||
|
||||
w = UnicodeWriter()
|
||||
w = add_header(w)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, add_days
|
||||
from erpnext.hr.doctype.leave_application.leave_application \
|
||||
import get_leave_balance_on, get_leaves_for_period
|
||||
|
||||
@@ -26,8 +26,8 @@ def get_columns(leave_types):
|
||||
for leave_type in leave_types:
|
||||
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
|
||||
columns.append(_(leave_type) + " " + _("Allocated") + ":Float:160")
|
||||
columns.append(_(leave_type) + " " + _("Expired") + ":Float:160")
|
||||
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
|
||||
columns.append(_(leave_type) + " " + _("Expired") + ":Float:160")
|
||||
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
|
||||
|
||||
return columns
|
||||
@@ -49,7 +49,7 @@ def get_data(filters, leave_types):
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
if filters.to_date <= filters.from_date:
|
||||
frappe.throw(_("From date can not be greater than than To date"))
|
||||
frappe.throw(_("'From Date should be less than To Date"))
|
||||
|
||||
active_employees = frappe.get_all("Employee",
|
||||
filters=conditions,
|
||||
@@ -84,7 +84,7 @@ def calculate_leaves_details(filters, leave_type, employee):
|
||||
# removing expired leaves
|
||||
leaves_taken = leaves_deducted - remove_expired_leave(ledger_entries)
|
||||
|
||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
||||
opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1))
|
||||
|
||||
new_allocation , expired_allocation = get_allocated_and_expired_leaves(ledger_entries, filters.from_date, filters.to_date)
|
||||
|
||||
@@ -94,7 +94,7 @@ def calculate_leaves_details(filters, leave_type, employee):
|
||||
#Formula for calculating closing balance
|
||||
closing = max(opening + new_allocation - (leaves_taken + expired_leaves), 0)
|
||||
|
||||
return [opening, new_allocation, expired_leaves, leaves_taken, closing]
|
||||
return [opening, new_allocation, leaves_taken, expired_leaves, closing]
|
||||
|
||||
|
||||
def remove_expired_leave(records):
|
||||
@@ -113,7 +113,7 @@ def get_allocated_and_expired_leaves(records, from_date, to_date):
|
||||
expired_leaves = 0
|
||||
|
||||
for record in records:
|
||||
if record.to_date <= getdate(to_date) and record.leaves>0:
|
||||
if record.to_date < getdate(to_date) and record.leaves>0:
|
||||
expired_leaves += record.leaves
|
||||
|
||||
if record.from_date >= getdate(from_date) and record.leaves>0:
|
||||
@@ -160,4 +160,4 @@ def get_department_leave_approver_map(department=None):
|
||||
for k, v in approver_list:
|
||||
approvers.setdefault(k, []).append(v)
|
||||
|
||||
return approvers
|
||||
return approvers
|
||||
|
||||
@@ -10,7 +10,7 @@ from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calc
|
||||
|
||||
def execute(filters=None):
|
||||
if filters.to_date <= filters.from_date:
|
||||
frappe.throw(_('From date can not be greater than than To date'))
|
||||
frappe.throw(_('From Date should be less than To Date'))
|
||||
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
@@ -45,16 +45,16 @@ def get_columns():
|
||||
'fieldtype': 'Float',
|
||||
'fieldname': 'new_allocation',
|
||||
'width': 120,
|
||||
}, {
|
||||
'label': _('Expired Leaves'),
|
||||
'fieldtype': 'Float',
|
||||
'fieldname': 'expired_leaves',
|
||||
'width': 120,
|
||||
}, {
|
||||
'label': _('Leaves Taken'),
|
||||
'fieldtype': 'float',
|
||||
'fieldname': 'leaves_taken',
|
||||
'width': 120,
|
||||
}, {
|
||||
'label': _('Expired Leaves'),
|
||||
'fieldtype': 'Float',
|
||||
'fieldname': 'expired_leaves',
|
||||
'width': 120,
|
||||
}, {
|
||||
'label': _('Closing Balance'),
|
||||
'fieldtype': 'float',
|
||||
@@ -96,8 +96,8 @@ def get_data(filters):
|
||||
leave_details = calculate_leaves_details(filters, leave_type, employee)
|
||||
row.opening_balance = flt(leave_details[0])
|
||||
row.new_allocation = flt(leave_details[1])
|
||||
row.expired_leaves = flt(leave_details[2])
|
||||
row.leaves_taken = flt(leave_details[3])
|
||||
row.leaves_taken = flt(leave_details[2])
|
||||
row.expired_leaves = flt(leave_details[3])
|
||||
row.closing_balance = flt(leave_details[4])
|
||||
|
||||
data.append(row)
|
||||
|
||||
@@ -101,8 +101,16 @@ class BOM(WebsiteGenerator):
|
||||
if self.routing:
|
||||
self.set("operations", [])
|
||||
for d in frappe.get_all("BOM Operation", fields = ["*"],
|
||||
filters = {'parenttype': 'Routing', 'parent': self.routing}):
|
||||
child = self.append('operations', d)
|
||||
filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"):
|
||||
child = self.append('operations', {
|
||||
"operation": d.operation,
|
||||
"workstation": d.workstation,
|
||||
"description": d.description,
|
||||
"time_in_mins": d.time_in_mins,
|
||||
"batch_size": d.batch_size,
|
||||
"operating_cost": d.operating_cost,
|
||||
"idx": d.idx
|
||||
})
|
||||
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
|
||||
|
||||
def validate_rm_item(self, item):
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.doctype == 'BOM'",
|
||||
"fieldname": "base_hour_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Base Hour Rate(Company Currency)",
|
||||
@@ -87,6 +88,7 @@
|
||||
},
|
||||
{
|
||||
"default": "5",
|
||||
"depends_on": "eval:parent.doctype == 'BOM'",
|
||||
"fieldname": "base_operating_cost",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Operating Cost(Company Currency)",
|
||||
@@ -108,12 +110,12 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2019-07-16 22:35:55.374037",
|
||||
"modified_by": "govindsmenokee@gmail.com",
|
||||
"modified": "2020-06-16 17:01:11.128420",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Operation",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', {
|
||||
name: d.workstation
|
||||
},
|
||||
callback: function (data) {
|
||||
frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate);
|
||||
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
||||
frm.events.calculate_operating_cost(frm, d);
|
||||
}
|
||||
|
||||
@@ -667,3 +667,7 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
|
||||
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
|
||||
erpnext.patches.v12_0.set_serial_no_status #2020-05-21
|
||||
erpnext.patches.v12_0.update_price_list_currency_in_bom
|
||||
erpnext.patches.v12_0.update_uom_conversion_factor
|
||||
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
|
||||
execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
|
||||
erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
''' Move from due_advance_amount to pending_amount '''
|
||||
|
||||
if frappe.db.has_column("Employee Advance", "due_advance_amount"):
|
||||
frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')
|
||||
@@ -0,0 +1,12 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from erpnext.regional.italy.setup import add_permissions
|
||||
|
||||
def execute():
|
||||
countries = frappe.get_all("Company", fields="country")
|
||||
countries = [country["country"] for country in countries]
|
||||
if "Italy" in countries:
|
||||
add_permissions()
|
||||
11
erpnext/patches/v12_0/update_uom_conversion_factor.py
Normal file
11
erpnext/patches/v12_0/update_uom_conversion_factor.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, json
|
||||
|
||||
def execute():
|
||||
from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data
|
||||
|
||||
frappe.reload_doc("setup", "doctype", "UOM Conversion Factor")
|
||||
frappe.reload_doc("setup", "doctype", "UOM")
|
||||
frappe.reload_doc("stock", "doctype", "UOM Category")
|
||||
|
||||
add_uom_data()
|
||||
@@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
import numpy as np
|
||||
from frappe.utils import cint
|
||||
from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
|
||||
|
||||
def get_field_filter_data():
|
||||
@@ -265,6 +266,8 @@ def get_next_attribute_and_values(item_code, selected_attributes):
|
||||
else:
|
||||
product_info = None
|
||||
|
||||
product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock)
|
||||
|
||||
return {
|
||||
'next_attribute': next_attribute,
|
||||
'valid_options_for_attributes': valid_options_for_attributes,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
setup: function() {
|
||||
this._super();
|
||||
frappe.flags.hide_serial_batch_dialog = true;
|
||||
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
var has_margin_field = frappe.meta.has_field(cdt, 'margin_type');
|
||||
@@ -539,7 +538,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
if (show_batch_dialog)
|
||||
return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
|
||||
.then((r) => {
|
||||
if(r.message.has_batch_no || r.message.has_serial_no) {
|
||||
if(r.message && !frappe.flags.hide_serial_batch_dialog &&
|
||||
(r.message.has_batch_no || r.message.has_serial_no)) {
|
||||
frappe.flags.hide_serial_batch_dialog = false;
|
||||
}
|
||||
});
|
||||
@@ -879,7 +879,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
|
||||
shipping_rule: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) {
|
||||
if(this.frm.doc.shipping_rule) {
|
||||
return this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: "apply_shipping_rule",
|
||||
@@ -1614,8 +1614,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
if(!r.exc) {
|
||||
$.each(me.frm.doc.items || [], function(i, item) {
|
||||
if(item.item_code && r.message.hasOwnProperty(item.item_code)) {
|
||||
item.item_tax_template = r.message[item.item_code].item_tax_template;
|
||||
item.item_tax_rate = r.message[item.item_code].item_tax_rate;
|
||||
if (!item.item_tax_template) {
|
||||
item.item_tax_template = r.message[item.item_code].item_tax_template;
|
||||
item.item_tax_rate = r.message[item.item_code].item_tax_rate;
|
||||
}
|
||||
me.add_taxes_from_item_tax_template(item.item_tax_rate);
|
||||
} else {
|
||||
item.item_tax_template = "";
|
||||
|
||||
@@ -62,7 +62,7 @@ erpnext.financial_statements = {
|
||||
}
|
||||
};
|
||||
|
||||
function get_filters(){
|
||||
function get_filters() {
|
||||
let filters = [
|
||||
{
|
||||
"fieldname":"company",
|
||||
@@ -129,15 +129,6 @@ function get_filters(){
|
||||
}
|
||||
]
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
filters.push({
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
@@ -191,6 +191,23 @@ $.extend(erpnext.utils, {
|
||||
})
|
||||
},
|
||||
|
||||
add_dimensions: function(report_name, index) {
|
||||
let filters = frappe.query_reports[report_name].filters;
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
let found = filters.some(el => el.fieldname === dimension['fieldname']);
|
||||
|
||||
if (!found) {
|
||||
filters.splice(index, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
make_subscription: function(doctype, docname) {
|
||||
frappe.call({
|
||||
method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat",
|
||||
@@ -470,7 +487,14 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
fieldtype: 'Date',
|
||||
fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
|
||||
in_list_view: 1,
|
||||
label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date")
|
||||
label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"),
|
||||
reqd: 1
|
||||
})
|
||||
fields.splice(3, 0, {
|
||||
fieldtype: 'Float',
|
||||
fieldname: "conversion_factor",
|
||||
in_list_view: 1,
|
||||
label: __("Conversion Factor")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -519,6 +543,7 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
"item_code": d.item_code,
|
||||
"delivery_date": d.delivery_date,
|
||||
"schedule_date": d.schedule_date,
|
||||
"conversion_factor": d.conversion_factor,
|
||||
"qty": d.qty,
|
||||
"rate": d.rate,
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe import _
|
||||
class QualityProcedure(NestedSet):
|
||||
nsm_parent_field = 'parent_quality_procedure'
|
||||
|
||||
def before_save(self):
|
||||
def on_save(self):
|
||||
for process in self.processes:
|
||||
if process.procedure:
|
||||
doc = frappe.get_doc("Quality Procedure", process.procedure)
|
||||
@@ -23,6 +23,11 @@ class QualityProcedure(NestedSet):
|
||||
|
||||
def after_insert(self):
|
||||
self.set_parent()
|
||||
#if Child is Added through Tree View.
|
||||
if self.parent_quality_procedure:
|
||||
parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
|
||||
parent_quality_procedure.append("processes", {"procedure": self.name})
|
||||
parent_quality_procedure.save()
|
||||
|
||||
def on_trash(self):
|
||||
if self.parent_quality_procedure:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2019-10-15 12:33:21.845329",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@@ -92,8 +91,7 @@
|
||||
"label": "Upload XML Invoices"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2019-12-10 16:37:26.793398",
|
||||
"modified": "2020-05-25 21:32:49.064579",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "Import Supplier Invoice",
|
||||
|
||||
@@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document):
|
||||
"buying_price_list": self.default_buying_price_list
|
||||
}
|
||||
|
||||
if not invoices_args.get("invoice_no", ''): return
|
||||
if not invoices_args.get("bill_no", ''):
|
||||
frappe.throw(_("Numero has not set in the XML file"))
|
||||
|
||||
supp_dict = get_supplier_details(file_content)
|
||||
invoices_args["destination_code"] = get_destination_code_from_file(file_content)
|
||||
|
||||
@@ -600,8 +600,9 @@ def get_transport_details(data, doc):
|
||||
data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy')
|
||||
|
||||
if doc.gst_transporter_id:
|
||||
validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID')
|
||||
data.transporterId = doc.gst_transporter_id
|
||||
if doc.gst_transporter_id[0:2] != "88":
|
||||
validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID')
|
||||
data.transporterId = doc.gst_transporter_id
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.permissions import add_permission, update_permission_property
|
||||
from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options
|
||||
|
||||
def setup(company=None, patch=True):
|
||||
make_custom_fields()
|
||||
setup_report()
|
||||
add_permissions()
|
||||
|
||||
def make_custom_fields(update=True):
|
||||
invoice_item_fields = [
|
||||
@@ -200,3 +202,21 @@ def setup_report():
|
||||
dict(role='Accounts Manager')
|
||||
]
|
||||
)).insert()
|
||||
|
||||
def add_permissions():
|
||||
doctype = 'Import Supplier Invoice'
|
||||
add_permission(doctype, 'All', 0)
|
||||
|
||||
for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'):
|
||||
add_permission(doctype, role, 0)
|
||||
update_permission_property(doctype, role, 0, 'print', 1)
|
||||
update_permission_property(doctype, role, 0, 'report', 1)
|
||||
|
||||
if role in ('Accounts Manager', 'Accounts User'):
|
||||
update_permission_property(doctype, role, 0, 'write', 1)
|
||||
update_permission_property(doctype, role, 0, 'create', 1)
|
||||
|
||||
update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1)
|
||||
add_permission(doctype, 'Accounts Manager', 1)
|
||||
update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1)
|
||||
update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1)
|
||||
@@ -117,12 +117,18 @@ class Gstr1Report(object):
|
||||
else:
|
||||
row.append(invoice_details.get(fieldname))
|
||||
taxable_value = 0
|
||||
|
||||
if invoice in self.cgst_igst_invoices:
|
||||
division_factor = 2
|
||||
else:
|
||||
division_factor = 1
|
||||
|
||||
for item_code, net_amount in self.invoice_items.get(invoice).items():
|
||||
if item_code in items:
|
||||
if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []):
|
||||
taxable_value += abs(net_amount)
|
||||
elif not self.item_tax_rate.get(invoice):
|
||||
taxable_value += abs(net_amount)
|
||||
if item_code in items:
|
||||
if self.item_tax_rate.get(invoice) and tax_rate/division_factor in self.item_tax_rate.get(invoice, {}).get(item_code, []):
|
||||
taxable_value += abs(net_amount)
|
||||
elif not self.item_tax_rate.get(invoice):
|
||||
taxable_value += abs(net_amount)
|
||||
|
||||
row += [tax_rate or 0, taxable_value]
|
||||
|
||||
@@ -196,7 +202,7 @@ class Gstr1Report(object):
|
||||
if d.item_code not in self.invoice_items.get(d.parent, {}):
|
||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
|
||||
sum(i.get('base_net_amount', 0) for i in items
|
||||
if i.item_code == d.item_code and i.parent == d.parent))
|
||||
if i.item_code == d.item_code and i.parent == d.parent))
|
||||
|
||||
item_tax_rate = {}
|
||||
|
||||
@@ -221,6 +227,8 @@ class Gstr1Report(object):
|
||||
|
||||
self.items_based_on_tax_rate = {}
|
||||
self.invoice_cess = frappe._dict()
|
||||
self.cgst_igst_invoices = []
|
||||
|
||||
unidentified_gst_accounts = []
|
||||
for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
|
||||
if account in self.gst_accounts.cess_account:
|
||||
@@ -243,6 +251,8 @@ class Gstr1Report(object):
|
||||
tax_rate = tax_amounts[0]
|
||||
if cgst_or_sgst:
|
||||
tax_rate *= 2
|
||||
if parent not in self.cgst_igst_invoices:
|
||||
self.cgst_igst_invoices.append(parent)
|
||||
|
||||
rate_based_dict = self.items_based_on_tax_rate\
|
||||
.setdefault(parent, {}).setdefault(tax_rate, [])
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax
|
||||
from six import iteritems
|
||||
|
||||
def update_itemised_tax_data(doc):
|
||||
if not doc.taxes: return
|
||||
@@ -9,7 +11,14 @@ def update_itemised_tax_data(doc):
|
||||
|
||||
for row in doc.items:
|
||||
tax_rate = 0.0
|
||||
if itemised_tax.get(row.item_code):
|
||||
item_tax_rate = frappe.parse_json(row.item_tax_rate)
|
||||
|
||||
# First check if tax rate is present
|
||||
# If not then look up in item_wise_tax_detail
|
||||
if item_tax_rate:
|
||||
for account, rate in iteritems(item_tax_rate):
|
||||
tax_rate += rate
|
||||
elif itemised_tax.get(row.item_code):
|
||||
tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
|
||||
|
||||
row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
|
||||
|
||||
@@ -282,6 +282,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"fetch_from": "customer.tax_id",
|
||||
"fieldname": "tax_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Tax Id",
|
||||
@@ -1196,7 +1197,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-17 12:50:39.640534",
|
||||
"modified": "2020-05-19 21:36:57.437325",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe
|
||||
],
|
||||
"field_no_map": [
|
||||
"rate",
|
||||
"price_list_rate"
|
||||
"price_list_rate",
|
||||
"item_tax_template"
|
||||
],
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items
|
||||
|
||||
@@ -334,7 +334,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(so.get("items")[-1].qty, 7)
|
||||
self.assertEqual(so.get("items")[-1].amount, 1400)
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
so = make_sales_order(**{
|
||||
"item_list": [{
|
||||
@@ -372,7 +372,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
"docname": so.get("items")[0].name
|
||||
}])
|
||||
update_child_qty_rate('Sales Order', trans_item, so.name)
|
||||
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(len(so.get("items")), 1)
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
@@ -399,6 +399,23 @@ class TestSalesOrder(unittest.TestCase):
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||
|
||||
def test_update_child_qty_rate_perm(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
test_user.add_roles("Accounts User")
|
||||
frappe.set_user(user)
|
||||
|
||||
# update qty
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||
|
||||
# add new item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_warehouse_user(self):
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
|
||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
||||
|
||||
@@ -150,6 +150,7 @@
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"columns": 2,
|
||||
"depends_on": "eval: !parent.skip_delivery_note",
|
||||
"fieldname": "delivery_date",
|
||||
@@ -209,6 +210,7 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "UOM",
|
||||
"options": "UOM",
|
||||
"print_hide": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -757,8 +759,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-05 14:20:28.085117",
|
||||
"modified": "2020-05-22 12:40:01.696076",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -229,7 +229,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
var me = this;
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
|
||||
if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) {
|
||||
let serial_no_count = item.serial_no
|
||||
? item.serial_no.split(`\n`).filter(d => d).length : 0;
|
||||
|
||||
if (item.serial_no && item.qty === serial_no_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1571,5 +1571,19 @@
|
||||
"to_uom": "Parts Per Million",
|
||||
"abbr": "ppm",
|
||||
"value": "10000"
|
||||
},
|
||||
{
|
||||
"category": "Mass",
|
||||
"from_uom": "Pound",
|
||||
"to_uom": "Ounce",
|
||||
"abbr": "oz",
|
||||
"value": "16"
|
||||
},
|
||||
{
|
||||
"category": "Mass",
|
||||
"from_uom": "Gram",
|
||||
"to_uom": "Ounce",
|
||||
"abbr": "oz",
|
||||
"value": "0.035274"
|
||||
}
|
||||
]
|
||||
@@ -335,13 +335,14 @@ def add_uom_data():
|
||||
"category_name": _(d.get("category"))
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
uom_conversion = frappe.get_doc({
|
||||
"doctype": "UOM Conversion Factor",
|
||||
"category": _(d.get("category")),
|
||||
"from_uom": _(d.get("from_uom")),
|
||||
"to_uom": _(d.get("to_uom")),
|
||||
"value": d.get("value")
|
||||
}).insert(ignore_permissions=True)
|
||||
if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}):
|
||||
uom_conversion = frappe.get_doc({
|
||||
"doctype": "UOM Conversion Factor",
|
||||
"category": _(d.get("category")),
|
||||
"from_uom": _(d.get("from_uom")),
|
||||
"to_uom": _(d.get("to_uom")),
|
||||
"value": d.get("value")
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def add_market_segments():
|
||||
records = [
|
||||
|
||||
@@ -78,8 +78,10 @@ def place_order():
|
||||
|
||||
if is_stock_item:
|
||||
item_stock = get_qty_in_stock(item.item_code, "website_warehouse")
|
||||
if not cint(item_stock.in_stock):
|
||||
throw(_("{1} Not in Stock").format(item.item_code))
|
||||
if item.qty > item_stock.stock_qty[0][0]:
|
||||
throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code))
|
||||
throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code))
|
||||
|
||||
sales_order.flags.ignore_permissions = True
|
||||
sales_order.insert()
|
||||
@@ -337,21 +339,17 @@ def set_price_list_and_rate(quotation, cart_settings):
|
||||
def _set_price_list(cart_settings, quotation=None):
|
||||
"""Set price list based on customer or shopping cart default"""
|
||||
from erpnext.accounts.party import get_default_price_list
|
||||
|
||||
# check if customer price list exists
|
||||
party_name = quotation.get("party_name") if quotation else get_party().get("name")
|
||||
selling_price_list = None
|
||||
if quotation and quotation.get("party_name"):
|
||||
selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list')
|
||||
|
||||
# else check for territory based price list
|
||||
# check if default customer price list exists
|
||||
if party_name:
|
||||
selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name))
|
||||
|
||||
# check default price list in shopping cart
|
||||
if not selling_price_list:
|
||||
selling_price_list = cart_settings.price_list
|
||||
|
||||
party_name = quotation.get("party_name") if quotation else get_party().get("name")
|
||||
|
||||
if not selling_price_list and party_name:
|
||||
selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name))
|
||||
|
||||
if quotation:
|
||||
quotation.selling_price_list = selling_price_list
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ def boot_session(bootinfo):
|
||||
"""boot session - send website info if guest"""
|
||||
|
||||
bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or ''
|
||||
bootinfo.website_settings = frappe.get_doc('Website Settings')
|
||||
|
||||
if frappe.session['user']!='Guest':
|
||||
update_page_info(bootinfo)
|
||||
|
||||
@@ -34,7 +34,7 @@ class ItemAttribute(Document):
|
||||
if self.numeric_values:
|
||||
validate_is_incremental(self, self.name, item.value, item.name)
|
||||
else:
|
||||
validate_item_attribute_value(attributes_list, self.name, item.value, item.name)
|
||||
validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False)
|
||||
|
||||
def validate_numeric(self):
|
||||
if self.numeric_values:
|
||||
|
||||
@@ -17,7 +17,7 @@ frappe.ui.form.on('Material Request', {
|
||||
|
||||
// formatter for material request item
|
||||
frm.set_indicator_formatter('item_code',
|
||||
function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; });
|
||||
function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; });
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', {
|
||||
};
|
||||
});
|
||||
frm.set_query('item_code', 'locations', () => {
|
||||
return erpnext.queries.item({ "is_stock_item": 1 });
|
||||
});
|
||||
frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => {
|
||||
const row = locals[cdt][cdn];
|
||||
return {
|
||||
query: 'erpnext.controllers.queries.get_batch_no',
|
||||
filters: {
|
||||
is_stock_item: 1
|
||||
}
|
||||
item_code: row.item_code,
|
||||
warehouse: row.warehouse
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -24,6 +24,9 @@ class PickList(Document):
|
||||
for item in self.locations:
|
||||
if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
|
||||
continue
|
||||
if not item.serial_no:
|
||||
frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format(
|
||||
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse))))
|
||||
if len(item.serial_no.split('\n')) == item.picked_qty:
|
||||
continue
|
||||
frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
|
||||
@@ -116,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map):
|
||||
if item_location.serial_no:
|
||||
serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)])
|
||||
|
||||
auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo")
|
||||
|
||||
locations.append(frappe._dict({
|
||||
'qty': qty,
|
||||
'stock_qty': stock_qty,
|
||||
'warehouse': item_location.warehouse,
|
||||
'serial_no': serial_nos,
|
||||
'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no,
|
||||
'batch_no': item_location.batch_no
|
||||
}))
|
||||
|
||||
@@ -203,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re
|
||||
sle.batch_no = batch.name
|
||||
and sle.`item_code`=%(item_code)s
|
||||
and sle.`company` = %(company)s
|
||||
and batch.disabled = 0
|
||||
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
|
||||
{warehouse_condition}
|
||||
GROUP BY
|
||||
@@ -468,4 +474,4 @@ def update_common_item_properties(item, location):
|
||||
item.material_request = location.material_request
|
||||
item.serial_no = location.serial_no
|
||||
item.batch_no = location.batch_no
|
||||
item.material_request_item = location.material_request_item
|
||||
item.material_request_item = location.material_request_item
|
||||
|
||||
@@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
|
||||
frm.custom_make_buttons = {
|
||||
'Stock Entry': 'Return',
|
||||
'Purchase Invoice': 'Invoice'
|
||||
'Purchase Invoice': 'Purchase Invoice'
|
||||
};
|
||||
|
||||
frm.set_query("expense_account", "items", function() {
|
||||
|
||||
@@ -178,7 +178,7 @@ class StockEntry(StockController):
|
||||
stock_items = self.get_stock_items()
|
||||
serialized_items = self.get_serialized_items()
|
||||
for item in self.get("items"):
|
||||
if item.qty and item.qty < 0:
|
||||
if flt(item.qty) and flt(item.qty) < 0:
|
||||
frappe.throw(_("Row {0}: The item {1}, quantity must be positive number")
|
||||
.format(item.idx, frappe.bold(item.item_code)))
|
||||
|
||||
@@ -497,7 +497,7 @@ class StockEntry(StockController):
|
||||
if raw_material_cost and self.purpose == "Manufacture":
|
||||
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
|
||||
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
|
||||
elif self.purpose == "Repack" and total_fg_qty:
|
||||
elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually:
|
||||
d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty)
|
||||
d.basic_amount = d.basic_rate * d.qty
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"image",
|
||||
"image_view",
|
||||
"quantity_and_rate",
|
||||
"set_basic_rate_manually",
|
||||
"qty",
|
||||
"basic_rate",
|
||||
"basic_amount",
|
||||
@@ -490,12 +491,21 @@
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse",
|
||||
"fieldname": "set_basic_rate_manually",
|
||||
"fieldtype": "Check",
|
||||
"label": "Set Basic Rate Manually",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-19 12:34:09.836295",
|
||||
"modified": "2020-06-08 12:57:03.172887",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry Detail",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user