def validate_allowed_dimensions(self): dimension_filter_map = get_dimension_filter_map() for key, value in iteritems(dimension_filter_map): dimension = key[0] account = key[1] if self.account == account: if value['is_mandatory'] and not self.get(dimension): frappe.throw( _("{0} is mandatory for account {1}").format( frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError) if value['allow_or_restrict'] == 'Allow': if self.get(dimension) and self.get( dimension) not in value['allowed_dimensions']: frappe.throw( _("Invalid value {0} for {1} against account {2}" ).format(frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError) else: if self.get(dimension) and self.get( dimension) in value['allowed_dimensions']: frappe.throw( _("Invalid value {0} for {1} against account {2}" ).format(frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
def validate_information(obj, attr, max_size): ''' Checks if the information is not set in the system and is within the size ''' if hasattr(obj, attr): return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size) else: frappe.throw( _("{0} is mandatory for generating remittance payments, set the field and try again" .format(frappe.unscrub(attr))))
def validate_information(obj, attr, max_size): ''' Checks if the information is not set in the system and is within the size ''' if getattr(obj, attr, None): form_link = get_link_to_form(obj.doctype, obj.name) return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size, form_link) else: frappe.throw( _("{0} is mandatory for generating remittance payments, set the field and try again" .format(frappe.unscrub(attr))))
def set_primary(self, fieldname): # Used to set primary mobile and phone no. if len(self.phone_nos) == 0: setattr(self, fieldname, "") return field_name = "is_primary_" + fieldname is_primary = [ phone.phone for phone in self.phone_nos if phone.get(field_name) ] if len(is_primary) > 1: frappe.throw( _("Only one {0} can be set as primary.").format( frappe.bold(frappe.unscrub(fieldname)))) primary_number_exists = False for d in self.phone_nos: if d.get(field_name) == 1: primary_number_exists = True setattr(self, fieldname, d.phone) break if not primary_number_exists: setattr(self, fieldname, "")
def validate_account_types(self): account_type_map = { 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' }, 'depreciation_expense_account': { 'root_type': 'Expense' }, 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' } } for d in self.accounts: for fieldname in account_type_map.keys(): if d.get(fieldname): selected_account = d.get(fieldname) key_to_match = next(iter(account_type_map.get( fieldname))) # acount_type or root_type selected_key_type = frappe.db.get_value( 'Account', selected_account, key_to_match) expected_key_type = account_type_map[fieldname][ key_to_match] if selected_key_type != expected_key_type: frappe.throw(_( "Row #{}: {} of {} should be {}. Please modify the account or select a different account." ).format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)), title=_("Invalid Account"))
def get_contact(doctype, name, contact_field): contact = frappe.db.get_value(doctype, name, contact_field) contact_persons = frappe.db.sql(""" SELECT parent, (SELECT is_primary_contact FROM tabContact c WHERE c.name = dl.parent) AS is_primary_contact FROM `tabDynamic Link` dl WHERE dl.link_doctype=%s AND dl.link_name=%s AND dl.parenttype = "Contact" """, (frappe.unscrub(contact_field), contact), as_dict=1) if contact_persons: for contact_person in contact_persons: contact_person.email_id = frappe.db.get_value( "Contact", contact_person.parent, ["email_id"]) if contact_person.is_primary_contact: return contact_person contact_person = contact_persons[0] return contact_person
def validate_account_currency(self): account_types = [ 'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account' ] invalid_accounts = [] for d in self.accounts: company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency') for type_of_account in account_types: if d.get(type_of_account): account_currency = frappe.get_value( "Account", d.get(type_of_account), "account_currency") if account_currency != company_currency: invalid_accounts.append( frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) })) for d in invalid_accounts: frappe.throw(_( "Row #{}: Currency of {} - {} doesn't matches company currency." ).format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)), title=_("Invalid Account"))
def get_category_records(categories): categorical_data = {} for category in categories: if category == "item_group": categorical_data["item_group"] = frappe.db.sql( """ Select name, parent_item_group, is_group, image, route from `tabItem Group` where parent_item_group = 'All Item Groups' and show_in_website = 1 """, as_dict=1, ) else: doctype = frappe.unscrub(category) fields = ["name"] if frappe.get_meta(doctype, cached=True).get_field("image"): fields += ["image"] categorical_data[category] = frappe.db.sql( f""" Select {",".join(fields)} from `tab{doctype}` """, as_dict=1, ) return categorical_data
def validate_account_types(self): account_type_map = { "fixed_asset_account": {"account_type": ["Fixed Asset"]}, "accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]}, "depreciation_expense_account": {"root_type": ["Expense", "Income"]}, "capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]}, } for d in self.accounts: for fieldname in account_type_map.keys(): if d.get(fieldname): selected_account = d.get(fieldname) key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type selected_key_type = frappe.db.get_value("Account", selected_account, key_to_match) expected_key_types = account_type_map[fieldname][key_to_match] if selected_key_type not in expected_key_types: frappe.throw( _( "Row #{}: {} of {} should be {}. Please modify the account or select a different account." ).format( d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_types), ), title=_("Invalid Account"), )
def get_contacts(email_strings): email_addrs = [] for email_string in email_strings: if email_string: for email in email_string.split(","): parsed_email = parseaddr(email)[1] if parsed_email: email_addrs.append(parsed_email) contacts = [] for email in email_addrs: email = get_email_without_link(email) contact_name = frappe.db.get_value('Contact', {'email_id': email}) if not contact_name: contact = frappe.get_doc({ "doctype": "Contact", "first_name": frappe.unscrub(email.split("@")[0]), "email_id": email }).insert(ignore_permissions=True) contact_name = contact.name contacts.append(contact_name) return contacts
def get_accounts_data(based_on, company): if based_on == "cost_center": return frappe.db.sql( """select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt from `tabCost Center` where company=%s order by name""", company, as_dict=True, ) elif based_on == "project": return frappe.get_all("Project", fields=["name"], filters={"company": company}, order_by="name") else: filters = {} doctype = frappe.unscrub(based_on) has_company = frappe.db.has_column(doctype, "company") if has_company: filters.update({"company": company}) return frappe.get_all(doctype, fields=["name"], filters=filters, order_by="name")
def validate_item_default_company_links( item_defaults: List[ItemDefault]) -> None: for item_default in item_defaults: for doctype, field in [ ["Warehouse", "default_warehouse"], ["Cost Center", "buying_cost_center"], ["Cost Center", "selling_cost_center"], ["Account", "expense_account"], ["Account", "income_account"], ]: if item_default.get(field): company = frappe.db.get_value(doctype, item_default.get(field), "company", cache=True) if company and company != item_default.company: frappe.throw( _("Row #{}: {} {} doesn't belong to Company {}. Please select valid {}." ).format( item_default.idx, doctype, frappe.bold(item_default.get(field)), frappe.bold(item_default.company), frappe.bold(frappe.unscrub(field)), ), title=_("Invalid Item Defaults"), )
def get_contacts(email_strings): email_addrs = [] for email_string in email_strings: if email_string: for email in email_string.split(","): parsed_email = parseaddr(email)[1] if parsed_email: email_addrs.append(parsed_email) contacts = [] for email in email_addrs: email = get_email_without_link(email) contact_name = get_contact_name(email) if not contact_name: contact = frappe.get_doc({ "doctype": "Contact", "first_name": frappe.unscrub(email.split("@")[0]), }) contact.add_email(email_id=email, is_primary=True) contact.insert(ignore_permissions=True) contact_name = contact.name contacts.append(contact_name) return contacts
def add_module_defs(app): modules = frappe.get_module_list(app) for module in modules: d = frappe.new_doc("Module Def") d.app_name = app d.module_name = frappe.unscrub(module) d.save()
def get_contacts(email_strings: List[str], auto_create_contact=False) -> List[str]: email_addrs = get_emails(email_strings) contacts = [] for email in email_addrs: email = get_email_without_link(email) contact_name = get_contact_name(email) if not contact_name and email and auto_create_contact: email_parts = email.split("@") first_name = frappe.unscrub(email_parts[0]) try: contact_name = ("{0}-{1}".format(first_name, email_parts[1]) if first_name == "Contact" else first_name) contact = frappe.get_doc({ "doctype": "Contact", "first_name": contact_name, "name": contact_name }) contact.add_email(email_id=email, is_primary=True) contact.insert(ignore_permissions=True) contact_name = contact.name except Exception: traceback = frappe.get_traceback() frappe.log_error(traceback) if contact_name: contacts.append(contact_name) return contacts
def add_module_defs(app): modules = frappe.get_module_list(app) for module in modules: d = frappe.new_doc("Module Def") d.app_name = app d.module_name = frappe.unscrub(module) d.save()
def check_illegal_depends_on_conditions(docfield): ''' assignment operation should not be allowed in the depends on condition.''' depends_on_fields = ["depends_on", "collapsible_depends_on"] for field in depends_on_fields: depends_on = docfield.get(field, None) if depends_on and ("=" in depends_on) and \ re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", depends_on): frappe.throw(_("Invalid {0} condition").format(frappe.unscrub(field)), frappe.ValidationError)
def check_illegal_depends_on_conditions(docfield): ''' assignment operation should not be allowed in the depends on condition.''' depends_on_fields = ["depends_on", "collapsible_depends_on"] for field in depends_on_fields: depends_on = docfield.get(field, None) if depends_on and ("=" in depends_on) and \ re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", depends_on): frappe.throw(_("Invalid {0} condition").format(frappe.unscrub(field)), frappe.ValidationError)
def get_columns(): pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id" columns = [{ "label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 90 }, { "label": _("Supplier"), "options": "Supplier", "fieldname": "supplier", "fieldtype": "Link", "width": 180 }, { "label": _("Section Code"), "options": "Tax Withholding Category", "fieldname": "section_code", "fieldtype": "Link", "width": 180 }, { "label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180 }, { "label": _("TDS Rate %"), "fieldname": "tds_rate", "fieldtype": "Float", "width": 90 }, { "label": _("Total Amount Credited"), "fieldname": "total_amount_credited", "fieldtype": "Float", "width": 90 }, { "label": _("Amount of TDS Deducted"), "fieldname": "tds_deducted", "fieldtype": "Float", "width": 90 }, { "label": _("Date of Transaction"), "fieldname": "transaction_date", "fieldtype": "Date", "width": 90 }, { "label": _("Transaction Type"), "fieldname": "transaction_type", "width": 90 }, { "label": _("Reference No."), "fieldname": "ref_no", "fieldtype": "Dynamic Link", "options": "transaction_type", "width": 90 }] return columns
def get_tax_account_head(shop_name: str, tax_type: str): tax_map = { "payout": "cash_bank_account", "refund": "cash_bank_account", "tax": "tax_account", "shipping": "shipping_account", "fee": "payment_fee_account", "adjustment": "payment_fee_account" } tax_field = tax_map.get(tax_type) if not tax_field: frappe.throw(_("Account not specified for '{0}'".format(frappe.unscrub(tax_type)))) tax_account = frappe.db.get_value("Shopify Settings", shop_name, tax_field) if not tax_account: frappe.throw(_("Account not specified for '{0}'".format(frappe.unscrub(tax_field)))) return tax_account
def not_in_user_permission(key, value, user=None): # returns true or false based on if value exist in user permission user = user or frappe.session.user user_permission = get_user_permissions(user).get(frappe.unscrub(key)) or [] for perm in user_permission: # doc found in user permission if perm.get('doc') == value: return False # return true only if user_permission exists return True if user_permission else False
def get_columns(filters): party_type = filters.get("party_type") party_type_value = get_party_group(party_type) return [ "{party_type}:Link/{party_type}".format(party_type=party_type), "{party_value_type}::150".format( party_value_type=frappe.unscrub(str(party_type_value))), "Address Line 1", "Address Line 2", "City", "State", "Postal Code", "Country", "Is Primary Address:Check", "First Name", "Last Name", "Phone", "Mobile No", "Email Id", "Is Primary Contact:Check" ]
def not_in_user_permission(key, value, user=None): # returns true or false based on if value exist in user permission user = user or frappe.session.user user_permission = get_user_permissions(user).get(frappe.unscrub(key)) or [] for perm in user_permission: # doc found in user permission if perm.get('doc') == value: return False # return true only if user_permission exists return True if user_permission else False
def clear_document(docname, fieldname): validate_document_fieldname(fieldname) project_name, project_timeline, client_view = frappe.db.get_value( "Project Document", docname, ['parent', 'project_timeline', 'client_view']) check_clear_attachment_permission() check_project_user_permission(project_name) check_project_timeline_permission(project_timeline, 'write') check_client_view_permission(client_view) project = frappe.get_doc("Project", project_name) document_row = project.get("documents", filters={"name": docname}) if not document_row: frappe.throw(_("Invalid Document Selected")) document_row = document_row[0] if document_row.document_status in [ "Approved", "Conditionally Approved", "Rejected" ]: frappe.throw( _("{0} document {1} version {2} {3} cannot be cleared because it is {4}" ).format(document_row.project_timeline, frappe.bold(document_row.document_name), document_row.document_version, frappe.unscrub(fieldname), frappe.bold(document_row.document_status))) if not document_row.get(fieldname): frappe.throw(_("Nothing to clear")) document_row.set(fieldname, "") project.save() frappe.msgprint( _("{0} document {1} version {2} {3} has been successfully <b>cleared</b>" ).format(document_row.project_timeline, frappe.bold(document_row.document_name), document_row.document_version, frappe.unscrub(fieldname)))
def execute(): # store data from original single document shopify_settings = frappe.get_doc("Shopify Settings") shopify_data = shopify_settings.as_dict(no_default_fields=True) shopify_password = shopify_settings.get_password("password") frappe.reload_doc("shopify_integration", "doctype", "shopify_settings") frappe.reload_doc("shopify_integration", "doctype", "shopify_payout") setup_custom_fields() # get shop name url = urlparse(shopify_data.get("shopify_url")) subdomain = url.hostname.split(".")[0] if subdomain: shop_name = frappe.unscrub(subdomain.replace("-", " ")) else: shop_name = "Shopify" # create new Shopify Settings document new_shop = frappe.new_doc("Shopify Settings") new_shop.update(shopify_data) new_shop.update({ "shop_name": shop_name, "password": shopify_password, "item_group": get_root_of("Item Group") }) new_shop.insert(ignore_permissions=True) # update Shopify Payout and linked Shopify documents for payout in frappe.get_all("Shopify Payout"): frappe.db.set_value("Shopify Payout", payout.name, "shop_name", new_shop.name) payout_doc = frappe.get_doc("Shopify Payout", payout.name) for transaction in payout_doc.transactions: if transaction.sales_order: frappe.db.set_value("Sales Order", transaction.sales_order, "shopify_settings", new_shop.name) if transaction.sales_invoice: frappe.db.set_value("Sales Invoice", transaction.sales_invoice, "shopify_settings", new_shop.name) if transaction.delivery_note: frappe.db.set_value("Delivery Note", transaction.delivery_note, "shopify_settings", new_shop.name) # # ref: https://github.com/ParsimonyGit/shipstation_integration/ # update the "Is Shopify Store" check in Shipstation stores if "shipstation_integration" in frappe.get_installed_apps(): mws_setup_marketplaces = frappe.get_all( "Shipstation Store", filters={"marketplace_name": "Shopify"}) for marketplace in mws_setup_marketplaces: frappe.db.set_value("Shipstation Store", marketplace.name, "is_shopify_store", True)
def cancel_shopify_order(shop_name: str, order: "Order", log_id: str = str()): """ Cancel all sales documents if a Shopify order is cancelled. Args: shop_name (str): The name of the Shopify configuration for the store. order (Order): The Shopify order data. log_id (str, optional): The ID of an existing Shopify Log. Defaults to an empty string. """ frappe.set_user("Administrator") frappe.flags.log_id = log_id doctypes = ["Delivery Note", "Sales Invoice", "Sales Order"] for doctype in doctypes: doc = get_shopify_document(shop_name=shop_name, doctype=doctype, order=order) if not doc: continue # recursively cancel all Shopify documents if doc.docstatus == 1: try: # ignore document links to Shopify Payout while cancelling doc.flags.ignore_links = True doc.cancel() except Exception as e: make_shopify_log(status="Error", response_data=order.to_dict(), exception=e, rollback=True) # update the financial status in all linked Shopify Payouts payout_transactions = frappe.get_all( "Shopify Payout Transaction", filters={ frappe.scrub(doctype): doc.name, "source_order_financial_status": ["!=", order.attributes.get("financial_status")] }) for transaction in payout_transactions: frappe.db.set_value( "Shopify Payout Transaction", transaction.name, "source_order_financial_status", frappe.unscrub(order.attributes.get("financial_status")))
def execute(filters=None): user, doctype, show_permissions = filters.get("user"), filters.get("doctype"), filters.get("show_permissions") if not validate(user, doctype): return [], [] columns, fields = get_columns_and_fields(doctype) data = frappe.get_list(doctype, fields=fields, as_list=True, user=user) if show_permissions: columns = columns + [frappe.unscrub(right) + ':Check:80' for right in rights] data = list(data) for i, doc in enumerate(data): permission = frappe.permissions.get_doc_permissions(frappe.get_doc(doctype, doc[0]), user) data[i] = doc + tuple(permission.get(right) for right in rights) return columns, data
def get_connection_class(python_module): filename = python_module.rsplit('.', 1)[-1] classname = frappe.unscrub(filename).replace(' ', '') module = frappe.get_module(python_module) raise_error = False if hasattr(module, classname): _class = getattr(module, classname) if not issubclass(_class, BaseConnection): raise_error = True else: raise_error = True if raise_error: raise ImportError(filename) return _class
def get_connection_class(python_module): filename = python_module.rsplit('.', 1)[-1] classname = frappe.unscrub(filename).replace(' ', '') module = frappe.get_module(python_module) raise_error = False if hasattr(module, classname): _class = getattr(module, classname) if not issubclass(_class, BaseConnection): raise_error = True else: raise_error = True if raise_error: raise ImportError(filename) return _class
def get_tabs(categories): tab_values = { "title": _("Shop by Category"), } categorical_data = get_category_records(categories) for index, tab in enumerate(categorical_data, start=1): tab_values[f"tab_{index + 1}_title"] = frappe.unscrub(tab) # pre-render cards for each tab tab_values[f"tab_{index + 1}_content"] = frappe.render_template( "erpnext/www/shop-by-category/category_card_section.html", { "data": categorical_data[tab], "type": tab }, ) return tab_values
def get_actual_expense(args): if not args.budget_against_doctype: args.budget_against_doctype = frappe.unscrub(args.budget_against_field) budget_against_field = args.get("budget_against_field") condition1 = " and gle.posting_date <= %(month_end_date)s" if args.get("month_end_date") else "" if args.is_tree: lft_rgt = frappe.db.get_value( args.budget_against_doctype, args.get(budget_against_field), ["lft", "rgt"], as_dict=1 ) args.update(lft_rgt) condition2 = """and exists(select name from `tab{doctype}` where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.{budget_against_field})""".format( doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec ) else: condition2 = """and exists(select name from `tab{doctype}` where name=gle.{budget_against} and gle.{budget_against} = %({budget_against})s)""".format( doctype=args.budget_against_doctype, budget_against=budget_against_field ) amount = flt( frappe.db.sql( """ select sum(gle.debit) - sum(gle.credit) from `tabGL Entry` gle where gle.account=%(account)s {condition1} and gle.fiscal_year=%(fiscal_year)s and gle.company=%(company)s and gle.docstatus=1 {condition2} """.format( condition1=condition1, condition2=condition2 ), (args), )[0][0] ) # nosec return amount
def add_customer_numbers(self, doc): from frappe import unscrub phone_map = { "mobile_1": self.get("mobile_1"), "mobile_2": self.get("mobile_2"), "mobile_3": self.get("mobile_3"), "aditional_number": self.get("aditional_number"), "other_number": self.get("other_number"), "landing_number": self.get("landing_number"), } for key in phone_map.keys(): if not phone_map[key]: continue doc.append("customer_phones", { "phone_number": phone_map[key], "description": unscrub(key) })
def get_columns(filters): party_type = filters.get("party_type") party_type_value = get_party_group(party_type) return [ "{party_type}:Link/{party_type}".format(party_type=party_type), "{party_value_type}::150".format(party_value_type = frappe.unscrub(str(party_type_value))), "Address Line 1", "Address Line 2", "City", "State", "Postal Code", "Country", "Is Primary Address:Check", "First Name", "Last Name", "Phone", "Mobile No", "Email Id", "Is Primary Contact:Check" ]
def validate_item_default_company_links( item_defaults: List[ItemDefault]) -> None: for item_default in item_defaults: for doctype, field in [['Warehouse', 'default_warehouse'], ['Cost Center', 'buying_cost_center'], ['Cost Center', 'selling_cost_center'], ['Account', 'expense_account'], ['Account', 'income_account']]: if item_default.get(field): company = frappe.db.get_value(doctype, item_default.get(field), 'company', cache=True) if company and company != item_default.company: frappe.throw(_( "Row #{}: {} {} doesn't belong to Company {}. Please select valid {}." ).format(item_default.idx, doctype, frappe.bold(item_default.get(field)), frappe.bold(item_default.company), frappe.bold(frappe.unscrub(field))), title=_("Invalid Item Defaults"))
def validate_information(obj, attr, max_size): ''' Checks if the information is not set in the system and is within the size ''' if getattr(obj, attr, None): val = getattr(obj, attr) if type(val).__name__ in ('int', 'float'): return validate_amount(val, max_size) else: return validate_field_size(val, frappe.unscrub(attr), max_size) elif not attr: if type(obj).__name__ in ('int', 'float'): return validate_amount(obj, max_size) return cstr(obj) else: if obj.doctype: link = obj.doctype +' '+ get_link_to_form(obj.doctype, obj.name) else: link = str(obj.name) frappe.throw(_("{0} in {1} is mandatory for generating file, set the field and try again").format(frappe.unscrub(attr), link))
def get_columns(filters): pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id" columns = [ { "label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 90 }, { "label": _("Supplier"), "options": "Supplier", "fieldname": "supplier", "fieldtype": "Link", "width": 180 }] if filters.naming_series == 'Naming Series': columns.append({ "label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 180 }) columns.extend([ { "label": _("Section Code"), "options": "Tax Withholding Category", "fieldname": "section_code", "fieldtype": "Link", "width": 180 }, { "label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180 }, { "label": _("TDS Rate %"), "fieldname": "tds_rate", "fieldtype": "Float", "width": 90 }, { "label": _("Total Amount Credited"), "fieldname": "total_amount_credited", "fieldtype": "Float", "width": 90 }, { "label": _("Amount of TDS Deducted"), "fieldname": "tds_deducted", "fieldtype": "Float", "width": 90 }, { "label": _("Date of Transaction"), "fieldname": "transaction_date", "fieldtype": "Date", "width": 90 }, { "label": _("Transaction Type"), "fieldname": "transaction_type", "width": 90 }, { "label": _("Reference No."), "fieldname": "ref_no", "fieldtype": "Dynamic Link", "options": "transaction_type", "width": 90 } ]) return columns