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.17.0'
|
||||
__version__ = '12.18.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -41,6 +41,8 @@ frappe.ui.form.on('Accounting Dimension', {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
frm.toggle_enable('document_type', frm.doc.__islocal);
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
|
||||
@@ -29,6 +29,16 @@ class AccountingDimension(Document):
|
||||
if exists and self.is_new():
|
||||
frappe.throw("Document Type already used as a dimension")
|
||||
|
||||
if not self.is_new():
|
||||
self.validate_document_type_change()
|
||||
|
||||
def validate_document_type_change(self):
|
||||
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
|
||||
if doctype_before_save != self.document_type:
|
||||
message = _("Cannot change Reference Document Type.")
|
||||
message += _("Please create a new Accounting Dimension if required.")
|
||||
frappe.throw(message)
|
||||
|
||||
def after_insert(self):
|
||||
if frappe.flags.in_test:
|
||||
make_dimension_in_accounting_doctypes(doc=self)
|
||||
|
||||
@@ -88,18 +88,18 @@ class PaymentReconciliation(Document):
|
||||
voucher_type = ('Sales Invoice'
|
||||
if self.party_type == 'Customer' else "Purchase Invoice")
|
||||
|
||||
return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type,
|
||||
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount,
|
||||
return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
|
||||
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount,
|
||||
account_currency as currency
|
||||
FROM `tab{doc}`, `tabGL Entry`
|
||||
FROM `tab{doc}` doc, `tabGL Entry` gl
|
||||
WHERE
|
||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
||||
and `tab{doc}`.{party_type_field} = %(party)s
|
||||
and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
|
||||
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
||||
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
||||
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
||||
GROUP BY `tab{doc}`.name
|
||||
(doc.name = gl.against_voucher or doc.name = gl.voucher_no)
|
||||
and doc.{party_type_field} = %(party)s
|
||||
and doc.is_return = 1 and ifnull(doc.return_against, "") = ""
|
||||
and gl.against_voucher_type = %(voucher_type)s
|
||||
and doc.docstatus = 1 and gl.party = %(party)s
|
||||
and gl.party_type = %(party_type)s and gl.account = %(account)s
|
||||
GROUP BY doc.name
|
||||
Having
|
||||
amount > 0
|
||||
""".format(
|
||||
@@ -303,4 +303,4 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
||||
]
|
||||
})
|
||||
|
||||
jv.submit()
|
||||
jv.submit()
|
||||
|
||||
@@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
|
||||
validate_disabled(doc)
|
||||
|
||||
# Validate with existing taxes and charges template for unique tax category
|
||||
validate_for_tax_category(doc)
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
validate_taxes_and_charges(tax)
|
||||
validate_inclusive_tax(tax, doc)
|
||||
@@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
def validate_disabled(doc):
|
||||
if doc.is_default and doc.disabled:
|
||||
frappe.throw(_("Disabled template must not be default template"))
|
||||
|
||||
def validate_for_tax_category(doc):
|
||||
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
|
||||
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
|
||||
|
||||
@@ -298,7 +298,8 @@ class Subscription(Document):
|
||||
Returns the `Item`s linked to `Subscription Plan`
|
||||
"""
|
||||
if prorate:
|
||||
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
|
||||
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
|
||||
self.generate_invoice_at_period_start)
|
||||
|
||||
items = []
|
||||
customer = self.customer
|
||||
@@ -468,11 +469,13 @@ class Subscription(Document):
|
||||
if invoice:
|
||||
return invoice.precision('grand_total')
|
||||
|
||||
|
||||
def get_prorata_factor(period_end, period_start):
|
||||
diff = flt(date_diff(nowdate(), period_start) + 1)
|
||||
plan_days = flt(date_diff(period_end, period_start) + 1)
|
||||
prorate_factor = diff / plan_days
|
||||
def get_prorata_factor(period_end, period_start, is_prepaid):
|
||||
if is_prepaid:
|
||||
prorate_factor = 1
|
||||
else:
|
||||
diff = flt(date_diff(nowdate(), period_start) + 1)
|
||||
plan_days = flt(date_diff(period_end, period_start) + 1)
|
||||
prorate_factor = diff / plan_days
|
||||
|
||||
return prorate_factor
|
||||
|
||||
|
||||
@@ -291,7 +291,8 @@ class TestSubscription(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
flt(
|
||||
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
|
||||
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
|
||||
subscription.generate_invoice_at_period_start),
|
||||
2),
|
||||
flt(prorate_factor, 2)
|
||||
)
|
||||
@@ -528,9 +529,7 @@ class TestSubscription(unittest.TestCase):
|
||||
current_inv = subscription.get_current_invoice()
|
||||
self.assertEqual(current_inv.status, "Unpaid")
|
||||
|
||||
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
|
||||
plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
|
||||
prorate_factor = flt(diff / plan_days)
|
||||
prorate_factor = 1
|
||||
|
||||
self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2))
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -15,15 +15,51 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Payment Document") + "::130",
|
||||
_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Cheque/Reference No") + "::120",
|
||||
_("Clearance Date") + ":Date:100",
|
||||
_("Against Account") + ":Link/Account:170",
|
||||
_("Amount") + ":Currency:120"
|
||||
]
|
||||
columns = [{
|
||||
"label": _("Payment Document Type"),
|
||||
"fieldname": "payment_document_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "Doctype",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"label": _("Payment Entry"),
|
||||
"fieldname": "payment_entry",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "payment_document_type",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Posting Date"),
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Cheque/Reference No"),
|
||||
"fieldname": "cheque_no",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Clearance Date"),
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Against Account"),
|
||||
"fieldname": "against",
|
||||
"fieldtype": "Link",
|
||||
"options": "Account",
|
||||
"width": 170
|
||||
},
|
||||
{
|
||||
"label": _("Amount"),
|
||||
"fieldname": "amount",
|
||||
"width": 120
|
||||
}]
|
||||
|
||||
return columns
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
@@ -206,7 +206,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
|
||||
|
||||
set_gl_entries_by_account(fiscal_year.year_start_date,
|
||||
fiscal_year.year_end_date, root.lft, root.rgt, filters,
|
||||
gl_entries_by_account, accounts_by_name, ignore_closing_entries=False)
|
||||
gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
|
||||
|
||||
calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters)
|
||||
accumulate_values_into_parents(accounts, accounts_by_name, companies)
|
||||
@@ -325,7 +325,7 @@ def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_curr
|
||||
return data
|
||||
|
||||
def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
|
||||
accounts_by_name, ignore_closing_entries=False):
|
||||
accounts_by_name, accounts, ignore_closing_entries=False):
|
||||
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
@@ -368,15 +368,31 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
|
||||
for entry in gl_entries:
|
||||
key = entry.account_number or entry.account_name
|
||||
validate_entries(key, entry, accounts_by_name)
|
||||
validate_entries(key, entry, accounts_by_name, accounts)
|
||||
gl_entries_by_account.setdefault(key, []).append(entry)
|
||||
|
||||
return gl_entries_by_account
|
||||
|
||||
def validate_entries(key, entry, accounts_by_name):
|
||||
def get_account_details(account):
|
||||
return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
|
||||
'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
|
||||
|
||||
def validate_entries(key, entry, accounts_by_name, accounts):
|
||||
if key not in accounts_by_name:
|
||||
field = "Account number" if entry.account_number else "Account name"
|
||||
frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
|
||||
args = get_account_details(entry.account)
|
||||
|
||||
if args.parent_account:
|
||||
parent_args = get_account_details(args.parent_account)
|
||||
|
||||
args.update({
|
||||
'lft': parent_args.lft + 1,
|
||||
'rgt': parent_args.rgt - 1,
|
||||
'root_type': parent_args.root_type,
|
||||
'report_type': parent_args.report_type
|
||||
})
|
||||
|
||||
accounts_by_name.setdefault(key, args)
|
||||
accounts.append(args)
|
||||
|
||||
def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
additional_conditions = []
|
||||
|
||||
@@ -54,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': item_record.item_name,
|
||||
'item_group': item_record.item_group,
|
||||
'item_name': item_record.item_name if item_record else d.item_name,
|
||||
'item_group': item_record.item_group if item_record else d.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
@@ -324,6 +324,7 @@ def get_items(filters, additional_query_columns):
|
||||
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||
|
||||
@@ -53,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': item_record.item_name,
|
||||
'item_group': item_record.item_group,
|
||||
'item_name': item_record.item_name if item_record else d.item_name,
|
||||
'item_group': item_record.item_group if item_record else d.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
@@ -390,6 +390,7 @@ def get_items(filters, additional_query_columns):
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
`tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`,
|
||||
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||
|
||||
@@ -1056,7 +1056,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-14 14:36:12.418690",
|
||||
"modified": "2021-01-22 20:27:11.418690",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
||||
|
||||
|
||||
def test_update_child_qty_rate(self):
|
||||
def test_update_child(self):
|
||||
mr = make_material_request(qty=10)
|
||||
po = make_purchase_order(mr.name)
|
||||
po.supplier = "_Test Supplier"
|
||||
@@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
def test_update_child_adding_new_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
@@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
def test_update_child_removing_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
@@ -185,7 +185,7 @@ 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):
|
||||
def test_update_child_perm(self):
|
||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
|
||||
@@ -108,6 +108,10 @@ class RequestforQuotation(BuyingController):
|
||||
'link_doctype': 'Supplier',
|
||||
'link_name': rfq_supplier.supplier
|
||||
})
|
||||
contact.append('email_ids', {
|
||||
'email_id': user.name,
|
||||
'is_primary': 1
|
||||
})
|
||||
|
||||
if not contact.email_id and not contact.user:
|
||||
contact.email_id = user.name
|
||||
|
||||
37
erpnext/change_log/v12/v12_18_0.md
Normal file
37
erpnext/change_log/v12/v12_18_0.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## ERPNext v12.18.0 Release Notes
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Make patient age translatable ([#24416](https://github.com/frappe/erpnext/pull/24416))
|
||||
- Adding UOM, Item Group via Update Items ([#24479](https://github.com/frappe/erpnext/pull/24479))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Incorrect incoming rate for the sales return ([#24620](https://github.com/frappe/erpnext/pull/24620))
|
||||
- Prorata factor fixes in subscription ([#24638](https://github.com/frappe/erpnext/pull/24638))
|
||||
- QR code image generation for e-invoicing ([#24422](https://github.com/frappe/erpnext/pull/24422))
|
||||
- Validation for disabled warehouse ([#24546](https://github.com/frappe/erpnext/pull/24546))
|
||||
- Update total in words after updating items ([#24592](https://github.com/frappe/erpnext/pull/24592))
|
||||
- Set contact email in RFQ ([#24486](https://github.com/frappe/erpnext/pull/24486))
|
||||
- Plaid client version to support latest API ([#24532](https://github.com/frappe/erpnext/pull/24532))
|
||||
- Dynamic Links for reports ([#24461](https://github.com/frappe/erpnext/pull/24461))
|
||||
- Use supplied year for IRS 1099 forms ([#24425](https://github.com/frappe/erpnext/pull/24425))
|
||||
- Add check for allowing access to european region ([#24393](https://github.com/frappe/erpnext/pull/24393))
|
||||
- Item-wise Sales Register item_name error ([#24484](https://github.com/frappe/erpnext/pull/24484))
|
||||
- Skip e-invoice generation for non-taxable invoices (India) ([#24569](https://github.com/frappe/erpnext/pull/24569))
|
||||
- Issues with packing items ([#24606](https://github.com/frappe/erpnext/pull/24606))
|
||||
- Remove max 5 file attachment limit in task ([#24056](https://github.com/frappe/erpnext/pull/24056))
|
||||
- Calculate discount amount ([#24511](https://github.com/frappe/erpnext/pull/24511))
|
||||
- Validate tax template for tax category ([#24403](https://github.com/frappe/erpnext/pull/24403))
|
||||
- Do not validate gstin for exports (India) ([#24564](https://github.com/frappe/erpnext/pull/24564))
|
||||
- Stock ageing should not take cancelled stock entries. ([#24438](https://github.com/frappe/erpnext/pull/24438))
|
||||
- Discount amount calculation on net total ([#24498](https://github.com/frappe/erpnext/pull/24498))
|
||||
- Fetching of standalone cr/dr notes for reconciliation ([#24576](https://github.com/frappe/erpnext/pull/24576))
|
||||
- Avoid changing Ref. Doctype in Accounting Dimension after creation ([#24579](https://github.com/frappe/erpnext/pull/24579))
|
||||
- Add GST state code for Ladakh (India) ([#24635](https://github.com/frappe/erpnext/pull/24635))
|
||||
- Consolidated Financial Statement report not works if child company accounts not present in the parent company ([#24580](https://github.com/frappe/erpnext/pull/24580))
|
||||
- Missing Asset Id in the Fixed Asset Register Report ([#24391](https://github.com/frappe/erpnext/pull/24391))
|
||||
- e_invoice print format not showing other charges ([#24473](https://github.com/frappe/erpnext/pull/24473))
|
||||
- Fix filters for report IRS 1099 ([#24597](https://github.com/frappe/erpnext/pull/24597))
|
||||
- Stock ledger entry was not created against stock reconciliation ([#24382](https://github.com/frappe/erpnext/pull/24382))
|
||||
- Validate cancellation only if irn generated (India) ([#24609](https://github.com/frappe/erpnext/pull/24609))
|
||||
@@ -1185,43 +1185,28 @@ def add_taxes_from_tax_template(child_item, parent_doc):
|
||||
})
|
||||
tax_row.db_insert()
|
||||
|
||||
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||
def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
|
||||
"""
|
||||
Returns a Sales Order Item child item containing the default values
|
||||
Returns a Sales/Purchase Order Item child item containing the default values
|
||||
"""
|
||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
|
||||
child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
|
||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||
child_item.item_code = item.item_code
|
||||
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
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
add_taxes_from_tax_template(child_item, p_doc)
|
||||
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.")
|
||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||
return child_item
|
||||
|
||||
|
||||
def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||
"""
|
||||
Returns a Purchase Order Item child item containing the default values
|
||||
"""
|
||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
|
||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||
child_item.item_code = item.item_code
|
||||
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.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
for field in ("item_code", "item_name", "description", "item_group"):
|
||||
child_item.update({field: item.get(field)})
|
||||
date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
|
||||
child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
|
||||
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||
if child_doctype == "Purchase Order Item":
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
if child_doctype == "Sales Order Item":
|
||||
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.")
|
||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
add_taxes_from_tax_template(child_item, p_doc)
|
||||
return child_item
|
||||
@@ -1285,8 +1270,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
)
|
||||
|
||||
def get_new_child_item(item_row):
|
||||
new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
|
||||
return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
|
||||
child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
|
||||
return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, 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):
|
||||
@@ -1316,6 +1301,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
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"))
|
||||
prev_uom, new_uom = child_item.get("uom"), d.get("uom")
|
||||
|
||||
if parent_doctype == 'Sales Order':
|
||||
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
||||
@@ -1324,9 +1310,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
|
||||
rate_unchanged = prev_rate == new_rate
|
||||
qty_unchanged = prev_qty == new_qty
|
||||
uom_unchanged = prev_uom == new_uom
|
||||
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:
|
||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
||||
continue
|
||||
|
||||
validate_quantity(child_item, d)
|
||||
@@ -1347,6 +1334,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
child_item.conversion_factor = 1
|
||||
else:
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision)
|
||||
|
||||
if d.get("uom"):
|
||||
child_item.uom = d.get("uom")
|
||||
conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor
|
||||
|
||||
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
||||
child_item.delivery_date = d.get('delivery_date')
|
||||
@@ -1388,6 +1380,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.flags.ignore_validate_update_after_submit = True
|
||||
parent.set_qty_as_per_stock_uom()
|
||||
parent.calculate_taxes_and_totals()
|
||||
parent.set_total_in_words()
|
||||
if parent_doctype == "Sales Order":
|
||||
make_packing_list(parent)
|
||||
parent.set_gross_profit()
|
||||
@@ -1413,6 +1406,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.update_receiving_percentage()
|
||||
if parent.is_subcontracted == "Yes":
|
||||
parent.update_reserved_qty_for_subcontract()
|
||||
parent.create_raw_materials_supplied("supplied_items")
|
||||
parent.save()
|
||||
else:
|
||||
parent.update_reserved_qty()
|
||||
parent.update_project()
|
||||
|
||||
@@ -310,12 +310,27 @@ class SellingController(StockController):
|
||||
if flt(d.conversion_factor)==0.0:
|
||||
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
|
||||
return_rate = 0
|
||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||
against_document_no = (d.get("sales_invoice_item")
|
||||
if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
|
||||
if cint(self.is_return) and self.docstatus==1:
|
||||
if self.return_against:
|
||||
against_document_no = (d.get("sales_invoice_item")
|
||||
if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
|
||||
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
self.return_against, against_document_no)
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
self.return_against, against_document_no)
|
||||
else:
|
||||
# For standalone credit note
|
||||
args = frappe._dict({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": flt(d.qty),
|
||||
"serial_no": d.serial_no,
|
||||
"company": d.company,
|
||||
"allow_zero_valuation": d.allow_zero_valuation
|
||||
})
|
||||
|
||||
return_rate = get_incoming_rate(args)
|
||||
|
||||
# On cancellation or if return entry submission, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
|
||||
@@ -319,12 +319,13 @@ class StockController(AccountsController):
|
||||
return incoming_rate
|
||||
|
||||
def validate_warehouse(self):
|
||||
from erpnext.stock.utils import validate_warehouse_company
|
||||
from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
|
||||
|
||||
warehouses = list(set([d.warehouse for d in
|
||||
self.get("items") if getattr(d, "warehouse", None)]))
|
||||
|
||||
for w in warehouses:
|
||||
validate_disabled_warehouse(w)
|
||||
validate_warehouse_company(w, self.company)
|
||||
|
||||
def update_billing_percentage(self, update_modified=True):
|
||||
|
||||
@@ -19,15 +19,50 @@ def set_defaut_value_for_filters(filters):
|
||||
if not filters.get('lead_age'): filters["lead_age"] = 60
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Lead") + ":Link/Lead:100",
|
||||
_("Name") + "::100",
|
||||
_("Organization") + "::100",
|
||||
_("Reference Document") + "::150",
|
||||
_("Reference Name") + ":Dynamic Link/"+_("Reference Document")+":120",
|
||||
_("Last Communication") + ":Data:200",
|
||||
_("Last Communication Date") + ":Date:180"
|
||||
]
|
||||
columns = [{
|
||||
"label": _("Lead"),
|
||||
"fieldname": "lead",
|
||||
"fieldtype": "Link",
|
||||
"options": "Lead",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"label": _("Name"),
|
||||
"fieldname": "name",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Organization"),
|
||||
"fieldname": "organization",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Reference Document Type"),
|
||||
"fieldname": "reference_document_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "Doctype",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Reference Name"),
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "reference_document_type",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Last Communication"),
|
||||
"fieldname": "last_communication",
|
||||
"fieldtype": "Data",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"label": _("Last Communication Date"),
|
||||
"fieldname": "last_communication_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
}]
|
||||
return columns
|
||||
|
||||
def get_data(filters):
|
||||
lead_details = []
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "question",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_list_view": 1,
|
||||
"label": "Question",
|
||||
"reqd": 1
|
||||
@@ -34,7 +34,7 @@
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"modified": "2019-05-30 18:39:21.880974",
|
||||
"modified": "2021-01-28 18:39:21.880974",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Education",
|
||||
"name": "Question",
|
||||
@@ -77,4 +77,4 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class PlaidConnector():
|
||||
client_id=self.settings.plaid_client_id,
|
||||
secret=self.settings.get_password("plaid_secret"),
|
||||
environment=self.settings.plaid_env,
|
||||
api_version="2019-05-29"
|
||||
api_version="2020-09-14"
|
||||
)
|
||||
|
||||
def get_access_token(self, public_token):
|
||||
@@ -29,7 +29,7 @@ class PlaidConnector():
|
||||
response = self.client.Item.public_token.exchange(public_token)
|
||||
access_token = response["access_token"]
|
||||
return access_token
|
||||
|
||||
|
||||
def get_token_request(self, update_mode=False):
|
||||
country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"]
|
||||
args = {
|
||||
|
||||
@@ -308,7 +308,7 @@ var calculate_age = function(birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
// List Stock items
|
||||
|
||||
@@ -293,5 +293,5 @@ var calculate_age = function(birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ frappe.ui.form.on('Patient', {
|
||||
$(frm.fields_dict['age_html'].wrapper).html("");
|
||||
}
|
||||
if(frm.doc.dob){
|
||||
$(frm.fields_dict['age_html'].wrapper).html("AGE : " + get_age(frm.doc.dob));
|
||||
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${get_age(frm.doc.dob)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -58,7 +58,7 @@ frappe.ui.form.on("Patient", "dob", function(frm) {
|
||||
}
|
||||
else{
|
||||
var age_str = get_age(frm.doc.dob);
|
||||
$(frm.fields_dict['age_html'].wrapper).html("AGE : " + age_str);
|
||||
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${age_str}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -81,7 +81,7 @@ var get_age = function (birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
var btn_create_vital_signs = function (frm) {
|
||||
|
||||
@@ -58,7 +58,7 @@ class Patient(Document):
|
||||
if self.dob:
|
||||
born = getdate(self.dob)
|
||||
age = dateutil.relativedelta.relativedelta(getdate(), born)
|
||||
age_str = str(age.years) + " year(s) " + str(age.months) + " month(s) " + str(age.days) + " day(s)"
|
||||
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
||||
return age_str
|
||||
|
||||
def invoice_patient_registration(self):
|
||||
|
||||
@@ -468,5 +468,5 @@ var calculate_age = function(birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
@@ -311,5 +311,5 @@ var calculate_age = function(birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
@@ -36,5 +36,5 @@ var calculate_age = function(birth) {
|
||||
var age = new Date();
|
||||
age.setTime(ageMS);
|
||||
var years = age.getFullYear() - 1970;
|
||||
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
@@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_leave_application_status
|
||||
erpnext.patches.v12_0.update_payment_entry_status
|
||||
erpnext.patches.v12_0.add_transporter_address_field #2020-10-27
|
||||
erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02
|
||||
erpnext.patches.v12_0.add_state_code_for_ladakh
|
||||
|
||||
16
erpnext/patches/v12_0/add_state_code_for_ladakh.py
Normal file
16
erpnext/patches/v12_0/add_state_code_for_ladakh.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import frappe
|
||||
from erpnext.regional.india import states
|
||||
|
||||
def execute():
|
||||
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
custom_fields = ['Address-gst_state', 'Tax Category-gst_state']
|
||||
|
||||
# Update options in gst_state custom fields
|
||||
for field in custom_fields:
|
||||
gst_state_field = frappe.get_doc('Custom Field', field)
|
||||
gst_state_field.options = '\n'.join(states)
|
||||
gst_state_field.save()
|
||||
792
erpnext/projects/doctype/task/task.json
Normal file → Executable file
792
erpnext/projects/doctype/task/task.json
Normal file → Executable file
@@ -1,398 +1,398 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "TASK-.YYYY.-.#####",
|
||||
"creation": "2013-01-29 19:25:50",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"subject",
|
||||
"project",
|
||||
"issue",
|
||||
"type",
|
||||
"is_group",
|
||||
"column_break0",
|
||||
"status",
|
||||
"priority",
|
||||
"task_weight",
|
||||
"completed_by",
|
||||
"color",
|
||||
"parent_task",
|
||||
"sb_timeline",
|
||||
"exp_start_date",
|
||||
"expected_time",
|
||||
"column_break_11",
|
||||
"exp_end_date",
|
||||
"progress",
|
||||
"is_milestone",
|
||||
"sb_details",
|
||||
"description",
|
||||
"sb_depends_on",
|
||||
"depends_on",
|
||||
"depends_on_tasks",
|
||||
"sb_actual",
|
||||
"act_start_date",
|
||||
"actual_time",
|
||||
"column_break_15",
|
||||
"act_end_date",
|
||||
"sb_costing",
|
||||
"total_costing_amount",
|
||||
"total_expense_claim",
|
||||
"column_break_20",
|
||||
"total_billing_amount",
|
||||
"sb_more_info",
|
||||
"review_date",
|
||||
"closing_date",
|
||||
"column_break_22",
|
||||
"department",
|
||||
"company",
|
||||
"lft",
|
||||
"rgt",
|
||||
"old_parent"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Subject",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Project",
|
||||
"oldfieldname": "project",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Project",
|
||||
"remember_last_selected_value": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "issue",
|
||||
"fieldtype": "Link",
|
||||
"label": "Issue",
|
||||
"options": "Issue"
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Type",
|
||||
"options": "Task Type"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"default": "0",
|
||||
"fieldname": "is_group",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled"
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Priority",
|
||||
"oldfieldname": "priority",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Low\nMedium\nHigh\nUrgent",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Color",
|
||||
"label": "Color"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "parent_task",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Parent Task",
|
||||
"options": "Task",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.__islocal",
|
||||
"fieldname": "sb_timeline",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Timeline"
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected Start Date",
|
||||
"oldfieldname": "exp_start_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "expected_time",
|
||||
"fieldtype": "Float",
|
||||
"label": "Expected Time (in hours)",
|
||||
"oldfieldname": "exp_total_hrs",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fetch_from": "type.weight",
|
||||
"fieldname": "task_weight",
|
||||
"fieldtype": "Float",
|
||||
"label": "Weight"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "exp_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected End Date",
|
||||
"oldfieldname": "exp_end_date",
|
||||
"oldfieldtype": "Date",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "progress",
|
||||
"fieldtype": "Percent",
|
||||
"label": "% Progress",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_milestone",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Milestone"
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Details",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_preview": 1,
|
||||
"label": "Task Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text Editor",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_depends_on",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dependencies",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Table",
|
||||
"label": "Dependent Tasks",
|
||||
"options": "Task Depends On"
|
||||
},
|
||||
{
|
||||
"fieldname": "depends_on_tasks",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"label": "Depends on Tasks",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_actual",
|
||||
"fieldtype": "Section Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "act_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual Start Date (via Time Sheet)",
|
||||
"oldfieldname": "act_start_date",
|
||||
"oldfieldtype": "Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "actual_time",
|
||||
"fieldtype": "Float",
|
||||
"label": "Actual Time (in hours)",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "act_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual End Date (via Time Sheet)",
|
||||
"oldfieldname": "act_end_date",
|
||||
"oldfieldtype": "Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sb_costing",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Costing"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_costing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Costing Amount (via Time Sheet)",
|
||||
"oldfieldname": "actual_budget",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_expense_claim",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Expense Claim (via Expense Claim)",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_20",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_billing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billing Amount (via Time Sheet)",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sb_more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.status == \"Closed\" || doc.status == \"Pending Review\"",
|
||||
"fieldname": "review_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Review Date",
|
||||
"oldfieldname": "review_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.status == \"Closed\"",
|
||||
"fieldname": "closing_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Closing Date",
|
||||
"oldfieldname": "closing_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_22",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department"
|
||||
},
|
||||
{
|
||||
"fetch_from": "project.company",
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "lft",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "lft",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rgt",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "rgt",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "old_parent",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Old Parent",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "completed_by",
|
||||
"fieldtype": "Link",
|
||||
"label": "Completed By",
|
||||
"no_copy": 1,
|
||||
"options": "User"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-check",
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"max_attachments": 5,
|
||||
"modified": "2020-07-03 12:36:04.960457",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Task",
|
||||
"nsm_parent_field": "parent_task",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Projects User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "subject",
|
||||
"show_name_in_global_search": 1,
|
||||
"show_preview_popup": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "project",
|
||||
"title_field": "subject",
|
||||
"track_seen": 1
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "TASK-.YYYY.-.#####",
|
||||
"creation": "2013-01-29 19:25:50",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"subject",
|
||||
"project",
|
||||
"issue",
|
||||
"type",
|
||||
"is_group",
|
||||
"column_break0",
|
||||
"status",
|
||||
"priority",
|
||||
"task_weight",
|
||||
"completed_by",
|
||||
"color",
|
||||
"parent_task",
|
||||
"sb_timeline",
|
||||
"exp_start_date",
|
||||
"expected_time",
|
||||
"column_break_11",
|
||||
"exp_end_date",
|
||||
"progress",
|
||||
"is_milestone",
|
||||
"sb_details",
|
||||
"description",
|
||||
"sb_depends_on",
|
||||
"depends_on",
|
||||
"depends_on_tasks",
|
||||
"sb_actual",
|
||||
"act_start_date",
|
||||
"actual_time",
|
||||
"column_break_15",
|
||||
"act_end_date",
|
||||
"sb_costing",
|
||||
"total_costing_amount",
|
||||
"total_expense_claim",
|
||||
"column_break_20",
|
||||
"total_billing_amount",
|
||||
"sb_more_info",
|
||||
"review_date",
|
||||
"closing_date",
|
||||
"column_break_22",
|
||||
"department",
|
||||
"company",
|
||||
"lft",
|
||||
"rgt",
|
||||
"old_parent"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Subject",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Project",
|
||||
"oldfieldname": "project",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Project",
|
||||
"remember_last_selected_value": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "issue",
|
||||
"fieldtype": "Link",
|
||||
"label": "Issue",
|
||||
"options": "Issue"
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Type",
|
||||
"options": "Task Type"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"default": "0",
|
||||
"fieldname": "is_group",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled"
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Priority",
|
||||
"oldfieldname": "priority",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Low\nMedium\nHigh\nUrgent",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Color",
|
||||
"label": "Color"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "parent_task",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Parent Task",
|
||||
"options": "Task",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.__islocal",
|
||||
"fieldname": "sb_timeline",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Timeline"
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected Start Date",
|
||||
"oldfieldname": "exp_start_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "expected_time",
|
||||
"fieldtype": "Float",
|
||||
"label": "Expected Time (in hours)",
|
||||
"oldfieldname": "exp_total_hrs",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fetch_from": "type.weight",
|
||||
"fieldname": "task_weight",
|
||||
"fieldtype": "Float",
|
||||
"label": "Weight"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "exp_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected End Date",
|
||||
"oldfieldname": "exp_end_date",
|
||||
"oldfieldtype": "Date",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "progress",
|
||||
"fieldtype": "Percent",
|
||||
"label": "% Progress",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_milestone",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Milestone"
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Details",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_preview": 1,
|
||||
"label": "Task Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text Editor",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_depends_on",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dependencies",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Table",
|
||||
"label": "Dependent Tasks",
|
||||
"options": "Task Depends On"
|
||||
},
|
||||
{
|
||||
"fieldname": "depends_on_tasks",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"label": "Depends on Tasks",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_actual",
|
||||
"fieldtype": "Section Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "act_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual Start Date (via Time Sheet)",
|
||||
"oldfieldname": "act_start_date",
|
||||
"oldfieldtype": "Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "actual_time",
|
||||
"fieldtype": "Float",
|
||||
"label": "Actual Time (in hours)",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "act_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual End Date (via Time Sheet)",
|
||||
"oldfieldname": "act_end_date",
|
||||
"oldfieldtype": "Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sb_costing",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Costing"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_costing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Costing Amount (via Time Sheet)",
|
||||
"oldfieldname": "actual_budget",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_expense_claim",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Expense Claim (via Expense Claim)",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_20",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_billing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billing Amount (via Time Sheet)",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sb_more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.status == \"Closed\" || doc.status == \"Pending Review\"",
|
||||
"fieldname": "review_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Review Date",
|
||||
"oldfieldname": "review_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.status == \"Closed\"",
|
||||
"fieldname": "closing_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Closing Date",
|
||||
"oldfieldname": "closing_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_22",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department"
|
||||
},
|
||||
{
|
||||
"fetch_from": "project.company",
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "lft",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "lft",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rgt",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "rgt",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "old_parent",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Old Parent",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "completed_by",
|
||||
"fieldtype": "Link",
|
||||
"label": "Completed By",
|
||||
"no_copy": 1,
|
||||
"options": "User"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-check",
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"max_attachments": 0,
|
||||
"modified": "2020-07-03 12:36:04.960457",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Task",
|
||||
"nsm_parent_field": "parent_task",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Projects User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "subject",
|
||||
"show_name_in_global_search": 1,
|
||||
"show_preview_popup": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "project",
|
||||
"title_field": "subject",
|
||||
"track_seen": 1
|
||||
}
|
||||
@@ -24,6 +24,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
|
||||
if (item.discount_amount) {
|
||||
item.rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
|
||||
item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -469,6 +469,33 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
read_only: 0,
|
||||
disabled: 0,
|
||||
label: __('Item Code')
|
||||
}, {
|
||||
fieldtype:'Link',
|
||||
fieldname:'uom',
|
||||
options: 'UOM',
|
||||
read_only: 0,
|
||||
label: __('UOM'),
|
||||
reqd: 1,
|
||||
onchange: function () {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.get_item_details.get_conversion_factor",
|
||||
args: { item_code: this.doc.item_code, uom: this.value },
|
||||
callback: r => {
|
||||
if(!r.exc) {
|
||||
if (this.doc.conversion_factor == r.message.conversion_factor) return;
|
||||
|
||||
const docname = this.doc.docname;
|
||||
dialog.fields_dict.trans_items.df.data.some(doc => {
|
||||
if (doc.docname == docname) {
|
||||
doc.conversion_factor = r.message.conversion_factor;
|
||||
dialog.fields_dict.trans_items.grid.refresh();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}, {
|
||||
fieldtype:'Float',
|
||||
fieldname:"qty",
|
||||
@@ -552,6 +579,7 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
"conversion_factor": d.conversion_factor,
|
||||
"qty": d.qty,
|
||||
"rate": d.rate,
|
||||
"uom": d.uom
|
||||
});
|
||||
this.data = dialog.fields_dict.trans_items.df.data;
|
||||
dialog.fields_dict.trans_items.grid.refresh();
|
||||
|
||||
@@ -133,6 +133,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
() => me.update_batch_serial_no_items(),
|
||||
() => {
|
||||
refresh_field("items");
|
||||
refresh_field("packed_items");
|
||||
if (me.callback) {
|
||||
return me.callback(me.item);
|
||||
}
|
||||
@@ -147,7 +148,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
if (this.item.serial_no) {
|
||||
this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
|
||||
}
|
||||
|
||||
|
||||
if (this.has_batch && !this.has_serial_no && d.batch_no) {
|
||||
this.frm.doc.items.forEach(data => {
|
||||
if(data.item_code == d.item_code) {
|
||||
@@ -229,7 +230,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
this.map_row_values(row, batch, 'batch_no',
|
||||
'selected_qty', this.values.warehouse);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update_serial_no_item() {
|
||||
@@ -248,7 +249,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
filters: { 'name': ["in", selected_serial_nos]},
|
||||
fields: ["batch_no", "name"]
|
||||
}).then((data) => {
|
||||
// data = [{batch_no: 'batch-1', name: "SR-001"},
|
||||
// data = [{batch_no: 'batch-1', name: "SR-001"},
|
||||
// {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}]
|
||||
const batch_serial_map = data.reduce((acc, d) => {
|
||||
if (!acc[d['batch_no']]) acc[d['batch_no']] = [];
|
||||
@@ -296,6 +297,8 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
} else {
|
||||
row.warehouse = values.warehouse || warehouse;
|
||||
}
|
||||
|
||||
this.frm.dirty();
|
||||
},
|
||||
|
||||
update_total_qty: function() {
|
||||
|
||||
@@ -20,6 +20,7 @@ states = [
|
||||
'Jharkhand',
|
||||
'Karnataka',
|
||||
'Kerala',
|
||||
'Ladakh',
|
||||
'Lakshadweep Islands',
|
||||
'Madhya Pradesh',
|
||||
'Maharashtra',
|
||||
@@ -59,6 +60,7 @@ state_numbers = {
|
||||
"Jharkhand": "20",
|
||||
"Karnataka": "29",
|
||||
"Kerala": "32",
|
||||
"Ladakh": "38",
|
||||
"Lakshadweep Islands": "31",
|
||||
"Madhya Pradesh": "23",
|
||||
"Maharashtra": "27",
|
||||
@@ -80,4 +82,4 @@ state_numbers = {
|
||||
"West Bengal": "19",
|
||||
}
|
||||
|
||||
number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
|
||||
number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
|
||||
|
||||
@@ -22,6 +22,9 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
|
||||
if (!irn && !__unsaved) {
|
||||
const action = () => {
|
||||
if (frm.doc.__unsaved) {
|
||||
frappe.throw(__('Please save the document to generate IRN.'));
|
||||
}
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
|
||||
args: { doctype, docname: name },
|
||||
|
||||
@@ -11,6 +11,7 @@ import json
|
||||
import base64
|
||||
import frappe
|
||||
import traceback
|
||||
import io
|
||||
from frappe import _, bold
|
||||
from pyqrcode import create as qrcreate
|
||||
from frappe.integrations.utils import make_post_request, make_get_request
|
||||
@@ -19,11 +20,13 @@ from frappe.utils.data import cstr, cint, formatdate as format_date, flt, time_d
|
||||
|
||||
def validate_einvoice_fields(doc):
|
||||
einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable'))
|
||||
invalid_doctype = doc.doctype not in ['Sales Invoice']
|
||||
invalid_doctype = doc.doctype != 'Sales Invoice'
|
||||
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
|
||||
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
|
||||
no_taxes_applied = len(doc.get('taxes', [])) == 0
|
||||
|
||||
if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction: return
|
||||
if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
|
||||
return
|
||||
|
||||
if doc.docstatus == 0 and doc._action == 'save':
|
||||
if doc.irn:
|
||||
@@ -34,7 +37,7 @@ def validate_einvoice_fields(doc):
|
||||
elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
|
||||
frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
|
||||
|
||||
elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
|
||||
elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
|
||||
frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
|
||||
|
||||
def raise_document_name_too_long_error():
|
||||
@@ -160,7 +163,7 @@ def get_item_list(invoice):
|
||||
item.description = d.item_name.replace('"', '\\"')
|
||||
|
||||
item.qty = abs(item.qty)
|
||||
item.discount_amount = abs(item.discount_amount * item.qty)
|
||||
item.discount_amount = 0
|
||||
item.unit_rate = abs(item.base_net_amount / item.qty)
|
||||
item.gross_amount = abs(item.base_net_amount)
|
||||
item.taxable_value = abs(item.base_net_amount)
|
||||
@@ -220,11 +223,12 @@ def get_invoice_value_details(invoice):
|
||||
|
||||
if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
|
||||
invoice_value_details.base_total = abs(invoice.base_total)
|
||||
invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
|
||||
else:
|
||||
invoice_value_details.base_total = abs(invoice.base_net_total)
|
||||
# since tax already considers discount amount
|
||||
invoice_value_details.invoice_discount_amt = 0
|
||||
|
||||
# since tax already considers discount amount
|
||||
invoice_value_details.invoice_discount_amt = 0 # invoice.base_discount_amount
|
||||
invoice_value_details.round_off = invoice.base_rounding_adjustment
|
||||
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
|
||||
invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
|
||||
@@ -301,7 +305,7 @@ def validate_mandatory_fields(invoice):
|
||||
_('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
|
||||
title=_('Missing Fields')
|
||||
)
|
||||
if not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
|
||||
if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
|
||||
frappe.throw(
|
||||
_('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
|
||||
title=_('Missing Fields')
|
||||
@@ -328,7 +332,10 @@ def make_einvoice(invoice):
|
||||
|
||||
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
|
||||
if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
|
||||
shipping_details = get_party_details(invoice.shipping_address_name)
|
||||
if invoice.gst_category == 'Overseas':
|
||||
shipping_details = get_overseas_address_details(invoice.shipping_address_name)
|
||||
else:
|
||||
shipping_details = get_party_details(invoice.shipping_address_name)
|
||||
|
||||
if invoice.is_pos and invoice.base_paid_amount:
|
||||
payment_details = get_payment_details(invoice)
|
||||
@@ -440,7 +447,7 @@ class GSPConnector():
|
||||
self.irn_details_url = self.base_url + '/enriched/ei/api/invoice/irn'
|
||||
self.generate_irn_url = self.base_url + '/enriched/ei/api/invoice'
|
||||
self.gstin_details_url = self.base_url + '/enriched/ei/api/master/gstin'
|
||||
self.cancel_ewaybill_url = self.base_url + '/enriched/ei/api/ewayapi'
|
||||
self.cancel_ewaybill_url = self.base_url + '/enriched/ewb/ewayapi?action=CANEWB'
|
||||
self.generate_ewaybill_url = self.base_url + '/enriched/ei/api/ewaybill'
|
||||
|
||||
def get_credentials(self):
|
||||
@@ -675,6 +682,8 @@ class GSPConnector():
|
||||
'cancelRsnCode': reason,
|
||||
'cancelRmrk': remark
|
||||
}, indent=4)
|
||||
headers["username"] = headers["user_name"]
|
||||
del headers["user_name"]
|
||||
|
||||
try:
|
||||
res = self.make_request('post', self.cancel_ewaybill_url, headers, data)
|
||||
@@ -773,20 +782,21 @@ class GSPConnector():
|
||||
qrcode = self.invoice.signed_qr_code
|
||||
doctype = self.invoice.doctype
|
||||
docname = self.invoice.name
|
||||
filename = 'QRCode_{}.png'.format(docname).replace(os.path.sep, "__")
|
||||
|
||||
_file = frappe.new_doc('File')
|
||||
_file.update({
|
||||
'file_name': 'QRCode_{}.png'.format(docname.replace('/', '-')),
|
||||
'attached_to_doctype': doctype,
|
||||
'attached_to_name': docname,
|
||||
'content': str(base64.b64encode(os.urandom(64))),
|
||||
'is_private': 1
|
||||
})
|
||||
_file.insert()
|
||||
frappe.db.commit()
|
||||
qr_image = io.BytesIO()
|
||||
url = qrcreate(qrcode, error='L')
|
||||
abs_file_path = os.path.abspath(_file.get_full_path())
|
||||
url.png(abs_file_path, scale=2, quiet_zone=1)
|
||||
url.png(qr_image, scale=2, quiet_zone=1)
|
||||
_file = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_name": filename,
|
||||
"attached_to_doctype": doctype,
|
||||
"attached_to_name": docname,
|
||||
"attached_to_field": "qrcode_image",
|
||||
"is_private": 1,
|
||||
"content": qr_image.getvalue()})
|
||||
_file.save()
|
||||
frappe.db.commit()
|
||||
|
||||
self.invoice.qrcode_image = _file.file_url
|
||||
|
||||
|
||||
@@ -168,5 +168,10 @@
|
||||
"state_number": "37",
|
||||
"state_code": "AD",
|
||||
"state_name": "Andhra Pradesh (New)"
|
||||
},
|
||||
{
|
||||
"state_number": "38",
|
||||
"state_code": "LA",
|
||||
"state_name": "Ladakh"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
frappe.query_reports["IRS 1099"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
@@ -13,7 +13,7 @@ frappe.query_reports["IRS 1099"] = {
|
||||
"width": 80,
|
||||
},
|
||||
{
|
||||
"fieldname":"fiscal_year",
|
||||
"fieldname": "fiscal_year",
|
||||
"label": __("Fiscal Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
@@ -22,7 +22,7 @@ frappe.query_reports["IRS 1099"] = {
|
||||
"width": 80,
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"fieldname": "supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group",
|
||||
@@ -32,16 +32,16 @@ frappe.query_reports["IRS 1099"] = {
|
||||
},
|
||||
],
|
||||
|
||||
onload: function(query_report) {
|
||||
onload: function (query_report) {
|
||||
query_report.page.add_inner_button(__("Print IRS 1099 Forms"), () => {
|
||||
build_1099_print(query_report);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function build_1099_print(query_report){
|
||||
function build_1099_print(query_report) {
|
||||
let filters = JSON.stringify(query_report.get_values());
|
||||
let w = window.open('/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?' +
|
||||
'&filters=' + encodeURIComponent(filters));
|
||||
'&filters=' + encodeURIComponent(filters));
|
||||
// w.print();
|
||||
}
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _, _dict
|
||||
from frappe.utils import nowdate
|
||||
from frappe.utils.data import fmt_money
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
from PyPDF2 import PdfFileWriter
|
||||
|
||||
import frappe
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, nowdate
|
||||
from frappe.utils.data import fmt_money
|
||||
from frappe.utils.jinja import render_template
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.utils.print_format import read_multi_pdf
|
||||
from frappe.utils.jinja import render_template
|
||||
|
||||
IRS_1099_FORMS_FILE_EXTENSION = ".pdf"
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
filters = filters if isinstance(filters, _dict) else _dict(filters)
|
||||
filters = filters if isinstance(filters, frappe._dict) else frappe._dict(filters)
|
||||
if not filters:
|
||||
filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0])
|
||||
filters.setdefault('company', frappe.db.get_default("company"))
|
||||
|
||||
region = frappe.db.get_value("Company", fieldname = ["country"], filters = { "name": filters.company })
|
||||
region = frappe.db.get_value("Company",
|
||||
filters={"name": filters.company},
|
||||
fieldname=["country"])
|
||||
|
||||
if region != 'United States':
|
||||
return [],[]
|
||||
return [], []
|
||||
|
||||
data = []
|
||||
columns = get_columns()
|
||||
conditions = ""
|
||||
if filters.supplier_group:
|
||||
conditions += "AND s.supplier_group = %s" %frappe.db.escape(filters.get("supplier_group"))
|
||||
|
||||
data = frappe.db.sql("""
|
||||
SELECT
|
||||
s.supplier_group as "supplier_group",
|
||||
@@ -33,20 +43,25 @@ def execute(filters=None):
|
||||
s.tax_id as "tax_id",
|
||||
SUM(gl.debit_in_account_currency) AS "payments"
|
||||
FROM
|
||||
`tabGL Entry` gl INNER JOIN `tabSupplier` s
|
||||
`tabGL Entry` gl
|
||||
INNER JOIN `tabSupplier` s
|
||||
WHERE
|
||||
s.name = gl.party
|
||||
AND s.irs_1099 = 1
|
||||
AND gl.fiscal_year = %(fiscal_year)s
|
||||
AND gl.party_type = "Supplier"
|
||||
AND s.irs_1099 = 1
|
||||
AND gl.fiscal_year = %(fiscal_year)s
|
||||
AND gl.party_type = "Supplier"
|
||||
AND gl.company = %(company)s
|
||||
{conditions}
|
||||
|
||||
GROUP BY
|
||||
gl.party
|
||||
|
||||
|
||||
ORDER BY
|
||||
gl.party DESC""", {"fiscal_year": filters.fiscal_year,
|
||||
"supplier_group": filters.supplier_group,
|
||||
"company": filters.company}, as_dict=True)
|
||||
gl.party DESC""".format(conditions=conditions), {
|
||||
"fiscal_year": filters.fiscal_year,
|
||||
"company": filters.company
|
||||
}, as_dict=True)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
@@ -70,14 +85,13 @@ def get_columns():
|
||||
"fieldname": "tax_id",
|
||||
"label": _("Tax ID"),
|
||||
"fieldtype": "Data",
|
||||
"width": 120
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
|
||||
"fieldname": "payments",
|
||||
"label": _("Total Payments"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
"width": 200
|
||||
}
|
||||
]
|
||||
|
||||
@@ -87,23 +101,33 @@ def irs_1099_print(filters):
|
||||
if not filters:
|
||||
frappe._dict({
|
||||
"company": frappe.db.get_default("Company"),
|
||||
"fiscal_year": frappe.db.get_default("fiscal_year")})
|
||||
"fiscal_year": frappe.db.get_default("Fiscal Year")
|
||||
})
|
||||
else:
|
||||
filters = frappe._dict(json.loads(filters))
|
||||
|
||||
fiscal_year_doc = get_fiscal_year(fiscal_year=filters.fiscal_year, as_dict=True)
|
||||
fiscal_year = cstr(fiscal_year_doc.year_start_date.year)
|
||||
|
||||
company_address = get_payer_address_html(filters.company)
|
||||
company_tin = frappe.db.get_value("Company", filters.company, "tax_id")
|
||||
|
||||
columns, data = execute(filters)
|
||||
template = frappe.get_doc("Print Format", "IRS 1099 Form").html
|
||||
output = PdfFileWriter()
|
||||
|
||||
for row in data:
|
||||
row["fiscal_year"] = fiscal_year
|
||||
row["company"] = filters.company
|
||||
row["company_tin"] = company_tin
|
||||
row["payer_street_address"] = company_address
|
||||
row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html("Supplier", row.supplier)
|
||||
row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html(
|
||||
"Supplier", row.supplier)
|
||||
row["payments"] = fmt_money(row["payments"], precision=0, currency="USD")
|
||||
frappe._dict(row)
|
||||
pdf = get_pdf(render_template(template, row), output=output if output else None)
|
||||
frappe.local.response.filename = filters.fiscal_year + " " + filters.company + " IRS 1099 Forms"
|
||||
|
||||
frappe.local.response.filename = "{0} {1} IRS 1099 Forms{2}".format(filters.fiscal_year,
|
||||
filters.company, IRS_1099_FORMS_FILE_EXTENSION)
|
||||
frappe.local.response.filecontent = read_multi_pdf(output)
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
@@ -119,36 +143,45 @@ def get_payer_address_html(company):
|
||||
ORDER BY
|
||||
address_type="Postal" DESC, address_type="Billing" DESC
|
||||
LIMIT 1
|
||||
""", {"company": company}, as_dict=True)
|
||||
""", {"company": company}, as_dict=True)
|
||||
|
||||
address_display = ""
|
||||
if address_list:
|
||||
company_address = address_list[0]["name"]
|
||||
return frappe.get_doc("Address", company_address).get_display()
|
||||
else:
|
||||
return ""
|
||||
address_display = frappe.get_doc("Address", company_address).get_display()
|
||||
|
||||
return address_display
|
||||
|
||||
|
||||
def get_street_address_html(party_type, party):
|
||||
address_list = frappe.db.sql("""
|
||||
SELECT
|
||||
link.parent
|
||||
FROM `tabDynamic Link` link, `tabAddress` address
|
||||
WHERE link.parenttype = "Address"
|
||||
AND link.link_name = %(party)s
|
||||
ORDER BY address.address_type="Postal" DESC,
|
||||
FROM
|
||||
`tabDynamic Link` link,
|
||||
`tabAddress` address
|
||||
WHERE
|
||||
link.parenttype = "Address"
|
||||
AND link.link_name = %(party)s
|
||||
ORDER BY
|
||||
address.address_type="Postal" DESC,
|
||||
address.address_type="Billing" DESC
|
||||
LIMIT 1
|
||||
""", {"party": party}, as_dict=True)
|
||||
""", {"party": party}, as_dict=True)
|
||||
|
||||
street_address = city_state = ""
|
||||
if address_list:
|
||||
supplier_address = address_list[0]["parent"]
|
||||
doc = frappe.get_doc("Address", supplier_address)
|
||||
|
||||
if doc.address_line2:
|
||||
street = doc.address_line1 + "<br>\n" + doc.address_line2 + "<br>\n"
|
||||
street_address = doc.address_line1 + "<br>\n" + doc.address_line2 + "<br>\n"
|
||||
else:
|
||||
street = doc.address_line1 + "<br>\n"
|
||||
city = doc.city + ", " if doc.city else ""
|
||||
city = city + doc.state + " " if doc.state else city
|
||||
city = city + doc.pincode if doc.pincode else city
|
||||
city += "<br>\n"
|
||||
return street, city
|
||||
else:
|
||||
return "", ""
|
||||
street_address = doc.address_line1 + "<br>\n"
|
||||
|
||||
city_state = doc.city + ", " if doc.city else ""
|
||||
city_state = city_state + doc.state + " " if doc.state else city_state
|
||||
city_state = city_state + doc.pincode if doc.pincode else city_state
|
||||
city_state += "<br>\n"
|
||||
|
||||
return street_address, city_state
|
||||
|
||||
@@ -502,7 +502,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
make_delivery_note: function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
|
||||
frm: me.frm
|
||||
frm: this.frm
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -319,11 +319,14 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
||||
existing_reserved_qty_item2 + 20)
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
def test_update_child_adding_new_item(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
create_dn_against_so(so.name, 4)
|
||||
make_sales_invoice(so.name)
|
||||
|
||||
prev_total = so.get("base_total")
|
||||
prev_total_in_words = so.get("base_in_words")
|
||||
|
||||
first_item_of_so = so.get("items")[0]
|
||||
trans_item = json.dumps([
|
||||
{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
|
||||
@@ -339,7 +342,13 @@ class TestSalesOrder(unittest.TestCase):
|
||||
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):
|
||||
updated_total = so.get("base_total")
|
||||
updated_total_in_words = so.get("base_in_words")
|
||||
|
||||
self.assertEqual(updated_total, prev_total+1400)
|
||||
self.assertNotEqual(updated_total_in_words, prev_total_in_words)
|
||||
|
||||
def test_update_child_removing_item(self):
|
||||
so = make_sales_order(**{
|
||||
"item_list": [{
|
||||
"item_code": '_Test Item',
|
||||
@@ -382,7 +391,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
|
||||
def test_update_child_qty_rate(self):
|
||||
def test_update_child(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
create_dn_against_so(so.name, 4)
|
||||
make_sales_invoice(so.name)
|
||||
@@ -419,7 +428,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(so.items[0].rate, 200.34669)
|
||||
make_property_setter("Sales Order Item", "rate", "precision", precision, "Currency")
|
||||
|
||||
def test_update_child_qty_rate_perm(self):
|
||||
def test_update_child_perm(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
@@ -472,7 +481,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
workflow.is_active = 0
|
||||
workflow.save()
|
||||
|
||||
def test_update_child_qty_rate_product_bundle(self):
|
||||
def test_update_child_product_bundle(self):
|
||||
# test Update Items with product bundle
|
||||
if not frappe.db.exists("Item", "_Product Bundle Item"):
|
||||
bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
|
||||
@@ -492,6 +501,20 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so.reload()
|
||||
self.assertEqual(so.packed_items[0].qty, 4)
|
||||
|
||||
# test uom and conversion factor change
|
||||
update_uom_conv_factor = json.dumps([{
|
||||
'item_code': so.get("items")[0].item_code,
|
||||
'rate': so.get("items")[0].rate,
|
||||
'qty': so.get("items")[0].qty,
|
||||
'uom': "_Test UOM 1",
|
||||
'conversion_factor': 2,
|
||||
'docname': so.get("items")[0].name
|
||||
}])
|
||||
update_child_qty_rate('Sales Order', update_uom_conv_factor, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.packed_items[0].qty, 8)
|
||||
|
||||
def test_update_child_with_tax_template(self):
|
||||
"""
|
||||
Test Action: Create a SO with one item having its tax account head already in the SO.
|
||||
|
||||
@@ -25,10 +25,11 @@ class StockLedgerEntry(Document):
|
||||
|
||||
def validate(self):
|
||||
self.flags.ignore_submit_comment = True
|
||||
from erpnext.stock.utils import validate_warehouse_company
|
||||
from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
|
||||
self.validate_mandatory()
|
||||
self.validate_item()
|
||||
self.validate_batch()
|
||||
validate_disabled_warehouse(self.warehouse)
|
||||
validate_warehouse_company(self.warehouse, self.company)
|
||||
self.scrub_posting_time()
|
||||
self.validate_and_set_fiscal_year()
|
||||
|
||||
@@ -211,7 +211,8 @@ def get_stock_ledger_entries(filters):
|
||||
from `tabItem` {item_conditions}) item
|
||||
where item_code = item.name and
|
||||
company = %(company)s and
|
||||
posting_date <= %(to_date)s
|
||||
posting_date <= %(to_date)s and
|
||||
is_cancelled != 1
|
||||
{sle_conditions}
|
||||
order by posting_date, posting_time, sle.creation, actual_qty""" #nosec
|
||||
.format(item_conditions=get_item_conditions(filters),
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe import _
|
||||
import json
|
||||
from frappe.utils import flt, cstr, nowdate, nowtime
|
||||
from frappe.utils import flt, cstr, nowdate, nowtime, get_link_to_form
|
||||
|
||||
from six import string_types
|
||||
|
||||
@@ -279,6 +279,10 @@ def is_group_warehouse(warehouse):
|
||||
if frappe.db.get_value("Warehouse", warehouse, "is_group"):
|
||||
frappe.throw(_("Group node warehouse is not allowed to select for transactions"))
|
||||
|
||||
def validate_disabled_warehouse(warehouse):
|
||||
if frappe.db.get_value("Warehouse", warehouse, "disabled"):
|
||||
frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse)))
|
||||
|
||||
def update_included_uom_in_report(columns, result, include_uom, conversion_factors):
|
||||
if not include_uom or not conversion_factors:
|
||||
return
|
||||
|
||||
@@ -3,9 +3,9 @@ frappe
|
||||
gocardless-pro==1.11.0
|
||||
googlemaps==3.1.1
|
||||
pandas==0.24.2
|
||||
plaid-python==6.0.0
|
||||
plaid-python>=7.0.0
|
||||
PyGithub==1.44.1
|
||||
python-stdnum==1.12
|
||||
Unidecode==1.1.1
|
||||
WooCommerce==2.1.1
|
||||
pycryptodome==3.9.8
|
||||
pycryptodome==3.9.8
|
||||
|
||||
Reference in New Issue
Block a user