def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" if self.enable_incoming: if frappe.local.flags.in_test: incoming_mails = test_mails else: email_server = self.get_server(in_receive=True) if not email_server: return incoming_mails = email_server.get_messages() exceptions = [] for raw in incoming_mails: try: communication = self.insert_communication(raw) except SentEmailInInbox: frappe.db.rollback() except Exception: frappe.db.rollback() log("email_account.receive") exceptions.append(frappe.get_traceback()) else: frappe.db.commit() attachments = [d.file_name for d in communication._attachments] # TODO fix bug where it sends emails to 'Adminsitrator' during testing communication.notify(attachments=attachments, fetched_from_email_account=True) if exceptions: raise Exception, frappe.as_json(exceptions)
def import_json(file, defaults): with open(file, 'r') as f: data = json.loads(f.read()) could_not_create = [] try: for i, d in enumerate(data): try: import_doc(d, defaults) frappe.db.commit() except Exception, e: frappe.db.rollback() could_not_create.append(d) if 'Item Code is mandatory' in cstr(e): pass else: print frappe.get_traceback() finally: print '-'*80 print file print frappe.as_json(could_not_create)
def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3.""" if self.enable_incoming: if frappe.local.flags.in_test: incoming_mails = test_mails else: pop3 = self.get_pop3() incoming_mails = pop3.get_messages() exceptions = [] for raw in incoming_mails: try: communication = self.insert_communication(raw) except SentEmailInInbox: frappe.db.rollback() except Exception: frappe.db.rollback() exceptions.append(frappe.get_traceback()) else: frappe.db.commit() attachments = [d.file_name for d in communication._attachments] communication.notify(attachments=attachments, fetched_from_email_account=True) if exceptions: raise Exception, frappe.as_json(exceptions)
def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" if self.enable_incoming: exceptions = [] if frappe.local.flags.in_test: incoming_mails = test_mails else: email_server = self.get_server(in_receive=True) if not email_server: return try: incoming_mails = email_server.get_messages() except Exception as e: frappe.db.sql("update `tabEmail Account` set no_remaining = NULL where name = %s",(self.name), auto_commit=1) incoming_mails = [] for msg in incoming_mails: try: communication = self.insert_communication(msg) #self.notify_update() except SentEmailInInbox as e: frappe.db.rollback() if self.use_imap: self.handle_bad_emails(email_server, msg[1], msg[0], "sent email in inbox") except Exception as e: frappe.db.rollback() log('email_account.receive') if self.use_imap: self.handle_bad_emails(email_server, msg[1], msg[0], frappe.get_traceback()) exceptions.append(frappe.get_traceback()) else: frappe.db.commit() attachments = [d.file_name for d in communication._attachments] if communication.message_id and not communication.timeline_hide: first = frappe.db.get_value("Communication", {"message_id": communication.message_id},["name"],order_by="creation",as_dict=1) if first: if first.name != communication.name: frappe.db.sql("""update tabCommunication set timeline_hide =%s where name = %s""",(first.name,communication.name),auto_commit=1) if self.no_remaining == '0' and not frappe.local.flags.in_test: if communication.reference_doctype : if not communication.timeline_hide and not communication.unread_notification_sent: communication.notify(attachments=attachments, fetched_from_email_account=True) #notify if user is linked to account if len(incoming_mails)>0 and not frappe.local.flags.in_test: frappe.publish_realtime('new_email', {"account":self.email_account_name, "account_name": self.name, "number":len(incoming_mails)}) if exceptions: raise Exception, frappe.as_json(exceptions)
def export_json(doctype, path, filters=None, name=None): def post_process(out): del_keys = ('parent', 'parentfield', 'parenttype', 'modified_by', 'creation', 'owner', 'idx') for doc in out: for key in del_keys: if key in doc: del doc[key] for k, v in doc.items(): if isinstance(v, list): for child in v: for key in del_keys + ('docstatus', 'doctype', 'modified', 'name'): if key in child: del child[key] out = [] if name: out.append(frappe.get_doc(doctype, name).as_dict()) elif frappe.db.get_value("DocType", doctype, "issingle"): out.append(frappe.get_doc(doctype).as_dict()) else: for doc in frappe.get_all(doctype, fields=["name"], filters=filters, limit_page_length=0, order_by="creation asc"): out.append(frappe.get_doc(doctype, doc.name).as_dict()) post_process(out) dirname = os.path.dirname(path) if not os.path.exists(dirname): path = os.path.join('..', path) with open(path, "w") as outfile: outfile.write(frappe.as_json(out))
def get_value(self, doctype, fieldname=None, filters=None): return self.get_request({ "cmd": "frappe.client.get_value", "doctype": doctype, "fieldname": fieldname or "name", "filters": frappe.as_json(filters) })
def export_customizations(module, doctype, sync_on_migrate=0, with_permissions=0): """Export Custom Field and Property Setter for the current document to the app folder. This will be synced with bench migrate""" if not frappe.get_conf().developer_mode: raise Exception('Not developer mode') custom = {'custom_fields': [], 'property_setters': [], 'custom_perms': [], 'doctype': doctype, 'sync_on_migrate': 1} def add(_doctype): custom['custom_fields'] += frappe.get_all('Custom Field', fields='*', filters={'dt': _doctype}) custom['property_setters'] += frappe.get_all('Property Setter', fields='*', filters={'doc_type': _doctype}) add(doctype) if with_permissions: custom['custom_perms'] = frappe.get_all('Custom DocPerm', fields='*', filters={'parent': doctype}) # also update the custom fields and property setters for all child tables for d in frappe.get_meta(doctype).get_table_fields(): export_customizations(module, d.options, sync_on_migrate, with_permissions) if custom["custom_fields"] or custom["property_setters"] or custom["custom_perms"]: folder_path = os.path.join(get_module_path(module), 'custom') if not os.path.exists(folder_path): os.makedirs(folder_path) path = os.path.join(folder_path, scrub(doctype)+ '.json') with open(path, 'w') as f: f.write(frappe.as_json(custom)) frappe.msgprint(_('Customizations for <b>{0}</b> exported to:<br>{1}').format(doctype,path))
def make_error_snapshot(exception): if frappe.conf.disable_error_snapshot: return logger = frappe.get_logger() try: error_id = '{timestamp:s}-{ip:s}-{hash:s}'.format( timestamp=cstr(datetime.datetime.now()), ip=frappe.local.request_ip or '127.0.0.1', hash=frappe.generate_hash(length=3) ) snapshot_folder = get_error_snapshot_path() frappe.create_folder(snapshot_folder) snapshot_file_path = os.path.join(snapshot_folder, "{0}.json".format(error_id)) snapshot = get_snapshot(exception) with open(encode(snapshot_file_path), 'wb') as error_file: error_file.write(encode(frappe.as_json(snapshot))) logger.error('New Exception collected with id: {}'.format(error_id)) except Exception, e: logger.error('Could not take error snapshot: {0}'.format(e))
def export_customizations(module, doctype, sync_on_migrate=0): """Export Custom Field and Property Setter for the current document to the app folder. This will be synced with bench migrate""" if not frappe.get_conf().developer_mode: raise 'Not developer mode' custom = {'custom_fields': [], 'property_setters': [], 'doctype': doctype, 'sync_on_migrate': 1} def add(_doctype): custom['custom_fields'] += frappe.get_all('Custom Field', fields='*', filters={'dt': _doctype}) custom['property_setters'] += frappe.get_all('Property Setter', fields='*', filters={'doc_type': _doctype}) add(doctype) # add custom fields and property setters for all child tables for d in frappe.get_meta(doctype).get_table_fields(): add(d.options) folder_path = os.path.join(get_module_path(module), 'custom') if not os.path.exists(folder_path): os.makedirs(folder_path) path = os.path.join(folder_path, scrub(doctype)+ '.json') with open(path, 'w') as f: f.write(frappe.as_json(custom)) frappe.msgprint('Customizations exported to {0}'.format(path))
def confirm_payment(token): paypal_express_payment = frappe.get_doc("Paypal Express Payment", token) params = get_paypal_params() params.update({ "METHOD": "DoExpressCheckoutPayment", "PAYERID": paypal_express_payment.payerid, "TOKEN": paypal_express_payment.token, "PAYMENTREQUEST_0_PAYMENTACTION": "SALE", "PAYMENTREQUEST_0_AMT": paypal_express_payment.amount, "PAYMENTREQUEST_0_CURRENCYCODE": paypal_express_payment.currency }) try: response = get_api_response(params) except PaypalException, e: frappe.db.rollback() frappe.get_doc({ "doctype": "Paypal Log", "error": "{e}\n\n{traceback}".format(e=e, traceback=frappe.get_traceback()), "params": frappe.as_json(params) }).insert(ignore_permissions=True) frappe.db.commit() frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url("/paypal-express-failed")
def add_comment(self, comment_type, text=None, comment_by=None, link_doctype=None, link_name=None): """Add a comment to this document. :param comment_type: e.g. `Comment`. See Communication for more info.""" if comment_type=='Comment': out = frappe.get_doc({ "doctype":"Communication", "communication_type": "Comment", "sender": comment_by or frappe.session.user, "comment_type": comment_type, "reference_doctype": self.doctype, "reference_name": self.name, "content": text or comment_type, "link_doctype": link_doctype, "link_name": link_name }).insert(ignore_permissions=True) else: out = frappe.get_doc(dict( doctype='Version', ref_doctype= self.doctype, docname= self.name, data = frappe.as_json(dict(comment_type=comment_type, comment=text)) )) if comment_by: out.owner = comment_by out.insert(ignore_permissions=True) return out
def export_json(doctype, path, filters=None, name=None): def post_process(out): del_keys = ("parent", "parentfield", "parenttype", "modified_by", "creation", "owner", "idx") for doc in out: for key in del_keys: if key in doc: del doc[key] for k, v in doc.items(): if isinstance(v, list): for child in v: for key in del_keys + ("docstatus", "doctype", "modified", "name"): if key in child: del child[key] out = [] if name: out.append(frappe.get_doc(doctype, name).as_dict()) elif frappe.db.get_value("DocType", doctype, "issingle"): out.append(frappe.get_doc(doctype).as_dict()) else: for doc in frappe.get_all( doctype, fields=["name"], filters=filters, limit_page_length=0, order_by="creation asc" ): out.append(frappe.get_doc(doctype, doc.name).as_dict()) post_process(out) with open(path, "w") as outfile: outfile.write(frappe.as_json(out))
def get_context(context): if (frappe.session.user == "Guest" or frappe.db.get_value("User", frappe.session.user, "user_type")=="Website User"): frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) hooks = frappe.get_hooks() boot = frappe.sessions.get() # this needs commit csrf_token = frappe.sessions.get_csrf_token() frappe.db.commit() boot_json = frappe.as_json(boot) # remove script tags from boot boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json) return { "build_version": get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], "boot": boot if context.get("for_mobile") else boot_json, "csrf_token": csrf_token, "background_image": boot.user.background_image or boot.default_background_image, "google_analytics_id": frappe.conf.get("google_analytics_id") }
def insert(self, doc): '''Insert a document to the remote server :param doc: A dict or Document object to be inserted remotely''' res = self.session.post(self.url + "/api/resource/" + doc.get("doctype"), data={"data":frappe.as_json(doc)}, verify=self.verify) return self.post_process(res)
def update(self, doc): '''Update a remote document :param doc: dict or Document object to be updated remotely. `name` is mandatory for this''' url = self.url + "/api/resource/" + doc.get("doctype") + "/" + doc.get("name") res = self.session.put(url, data={"data":frappe.as_json(doc)}, verify=self.verify) return self.post_process(res)
def submit(self, doc): '''Submit remote document :param doc: dict or Document object to be submitted remotely''' return self.post_request({ "cmd": "frappe.client.submit", "doc": frappe.as_json(doc) })
def insert_many(self, docs): '''Insert multiple documents to the remote server :param docs: List of dict or Document objects to be inserted in one request''' return self.post_request({ "cmd": "frappe.client.insert_many", "docs": frappe.as_json(docs) })
def bulk_update(self, docs): '''Bulk update documents remotely :param docs: List of dict or Document objects to be updated remotely (by `name`)''' return self.post_request({ "cmd": "frappe.client.bulk_update", "docs": frappe.as_json(docs) })
def export_languages_json(): '''Export list of all languages''' languages = frappe.db.get_all('Language', fields=['name', 'language_name']) languages = [{'name': d.language_name, 'code': d.name} for d in languages] languages.sort(key = lambda a: a['code']) with open(frappe.get_app_path('frappe', 'geo', 'languages.json'), 'w') as f: f.write(frappe.as_json(languages))
def set_diff(self, old, new): '''Set the data property with the diff of the docs if present''' diff = get_diff(old, new) if diff: self.ref_doctype = new.doctype self.docname = new.name self.data = frappe.as_json(diff) return True else: return False
def emit_via_redis(event, message, room): """Publish real-time updates via redis :param event: Event name, like `task_progress` etc. :param message: JSON message object. For async must contain `task_id` :param room: name of the room""" r = get_redis_server() try: r.publish('events', frappe.as_json({'event': event, 'message': message, 'room': room})) except redis.exceptions.ConnectionError: pass
def receive(self): """Called by scheduler to receive emails from this EMail account using POP3.""" if self.enabled: pop3 = self.get_pop3() incoming_mails = pop3.get_messages() exceptions = [] for raw in incoming_mails: try: self.insert_communication(raw) except Exception: frappe.db.rollback() exceptions.append(frappe.get_traceback()) else: frappe.db.commit() if exceptions: raise Exception, frappe.as_json(exceptions)
def get_value(self, doctype, fieldname=None, filters=None): '''Returns a value form a document :param doctype: DocType to be queried :param fieldname: Field to be returned (default `name`) :param filters: dict or string for identifying the record''' return self.get_request({ "cmd": "frappe.client.get_value", "doctype": doctype, "fieldname": fieldname or "name", "filters": frappe.as_json(filters) })
def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, lang=None, session=None,read_receipt=None): try: frappe.connect(site=site) if lang: frappe.local.lang = lang if session: # hack to enable access to private files in PDF session['data'] = frappe._dict(session['data']) frappe.local.session.update(session) # upto 3 retries for i in xrange(3): try: communication = frappe.get_doc("Communication", communication_name) if communication.sent_or_received == "Received": communication.message_id = None communication._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc) except MySQLdb.OperationalError, e: # deadlock, try again if e.args[0]==1213: frappe.db.rollback() time.sleep(1) continue else: raise else: break except: traceback = log("frappe.tasks.sendmail", frappe.as_json({ "site": site, "communication_name": communication_name, "print_html": print_html, "print_format": print_format, "attachments": attachments, "recipients": recipients, "cc": cc, "lang": lang })) task_logger.error(traceback) raise else: frappe.db.commit() finally: frappe.destroy()
def add_sidebars(self): '''Add _sidebar.json in each folder in docs''' for basepath, folders, files in os.walk(self.docs_path): # pylint: disable=unused-variable with open(os.path.join(basepath, '_sidebar.json'), 'w') as sidebarfile: sidebarfile.write(frappe.as_json([ {"title": "Search Docs ...", "type": "input", "route": "/search_docs"}, {"title": "Docs Home", "route": "/docs"}, {"title": "User Guide", "route": "/docs/user"}, {"title": "Server API", "route": "/docs/current/api"}, {"title": "Models (Reference)", "route": "/docs/current/models"}, {"title": "Improve Docs", "route": "{0}/tree/develop/{1}/docs".format(self.docs_config.source_link, self.app)} ]))
def receive(self): """Called by scheduler to receive emails from this EMail account using imap.""" if self.enabled: imap = self.get_imap() incoming_mails = imap.get_messages() print incoming_mails exceptions = [] account_name = self.email_account_name for raw in incoming_mails: try: self.insert_communication(raw, account_name) except Exception: frappe.db.rollback() exceptions.append(frappe.get_traceback()) else: frappe.db.commit() if exceptions: raise Exception, frappe.as_json(exceptions)
def emit_via_redis(event, message, room): """Publish real-time updates via redis :param event: Event name, like `task_progress` etc. :param message: JSON message object. For async must contain `task_id` :param room: name of the room""" r = get_redis_server() try: r.publish("events", frappe.as_json({"event": event, "message": message, "room": room})) except redis.exceptions.ConnectionError: # print frappe.get_traceback() pass
def call(fn, *args, **kwargs): """ Pass a doctype or a series of doctypes to get the count of docs in them Parameters: fn: frappe function to be called Returns: based on the function you call: output of the function you call Example: via terminal: bench --site erpnext.local execute frappe.utils.call --args '''["frappe.get_all", "Activity Log"]''' --kwargs '''{"fields": ["user", "creation", "full_name"], "filters":{"Operation": "Login", "Status": "Success"}, "limit": "10"}''' """ return json.loads(frappe.as_json(frappe.call(fn, *args, **kwargs)))
def create_json_gz_file(data, dt, dn): # Storing data in CSV file causes information loss # Reports like P&L Statement were completely unsuable because of this json_filename = '{0}.json.gz'.format(frappe.utils.data.format_datetime(frappe.utils.now(), "Y-m-d-H:M")) encoded_content = frappe.safe_encode(frappe.as_json(data)) # GZip compression seems to reduce storage requirements by 80-90% compressed_content = gzip_compress(encoded_content) save_file( fname=json_filename, content=compressed_content, dt=dt, dn=dn, folder=None, is_private=False)
def set_express_checkout(amount, currency="USD", data=None): validate_transaction_currency(currency) if not isinstance(data, basestring): data = frappe.as_json(data or "{}") response = execute_set_express_checkout(amount, currency) if not response["success"]: paypal_log(response) frappe.db.commit() frappe.respond_as_web_page(_("Something went wrong"), _("Looks like something is wrong with this site's Paypal configuration. Don't worry! No payment has been made from your Paypal account."), success=False, http_status_code=frappe.ValidationError.http_status_code) return paypal_settings = get_paypal_settings() if paypal_settings.paypal_sandbox: return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" else: return_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" token = response.get("TOKEN")[0] paypal_express_payment = frappe.get_doc({ "doctype": "Paypal Express Payment", "status": "Started", "amount": amount, "currency": currency, "token": token, "data": data, "correlation_id": response.get("CORRELATIONID")[0] }) if data: if isinstance(data, basestring): data = json.loads(data) if data.get("doctype") and data.get("docname"): paypal_express_payment.reference_doctype = data.get("doctype") paypal_express_payment.reference_docname = data.get("docname") paypal_express_payment.insert(ignore_permissions = True) frappe.db.commit() frappe.local.response["type"] = "redirect" frappe.local.response["location"] = return_url.format(token)
def __init__(self): super(JSInterpreter, self).__init__() # load javascript path for app in frappe.get_installed_apps(): for hook in frappe.get_hooks('studio_library_path', app_name=app): self.loader.register_path(frappe.get_app_path(app, hook)) # load functions _gbl = tree() replacements = {} for attr in frappe.get_hooks('studio_functions', []): paths = [] if isinstance(attr, dict): for key, value in attr.items(): attr, expand = get_attr(value) if not expand: paths.append(key) self.export_function(key, attr) else: base_path = key for fn, item in inspect.getmembers( attr, is_module_function(base_path)): key = '{0}.{1}'.format(base_path, fn) self.export_function(key, item) paths.append(key) elif isinstance(attr, (list, tuple, set)): raise frappe.ValidationError( 'Invalid hook format {}, should be ("list" or "dict") not "{}"' .format(frappe.as_json(list(attr)), type(attr).__name__)) else: obj, expand = get_attr(attr) if not expand: paths.append(attr) self.export_function(attr, obj) else: base_path = attr for fn, item in inspect.getmembers( obj, is_module_function(base_path)): attr = '{0}.{1}'.format(base_path, fn) self.export_function(key, item) paths.append(attr) for path in paths: parts = path.split('.') fn = parts.pop() actual = None for part in parts: actual = (actual or _gbl)[part] actual[fn] = '{{{0}}}'.format(path.replace('.', '_')) replacements[path.replace( '.', '_' )] = '''function() {{ return call_python("{0}", as_list(arguments)); }}'''.format( path) self.evaljs(''' function as_list(a){ var args = []; for(var i = 0; i < a.length; i++){ args.push(a[i]); } return args; } function enable_document_syncronization(){ ctx.enabled_document_syncronization = true; } function disable_document_syncronization(){ ctx.enabled_document_syncronization = false; } function add_child(field, child){ if (!ctx.enabled_document_syncronization) return; var df = frappe.utils.filter_dict(ctx.meta.fields, {'fieldname': field, 'fieldtype': 'Table'}); if (!df) return; df = df[0]; if (!Array.isArray(doc[df.fieldname])) doc[df.fieldname] = []; if (!child.doctype) child.doctype = df.options; if (!child.parenttype) child.parenttype = doc.doctype; if (!child.paerentfield) child.parentfield = df.fieldname; doc[df.fieldname].push(child); } ''') JS_GLOBALS = [] for k in _gbl.keys(): JS_GLOBALS_PART = k + ' = ' + json.dumps(_gbl[k], indent=2) + ';' for rk, v in replacements.items(): if not rk.startswith(k + '_'): continue JS_GLOBALS_PART = JS_GLOBALS_PART.replace('"{' + rk + '}"', v) JS_GLOBALS.append(JS_GLOBALS_PART) #frappe.msgprint('<pre>{0}</pre>'.format('\n'.join(JS_GLOBALS))) self.evaljs('\n'.join(JS_GLOBALS))
def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, lang=None, session=None): try: frappe.connect(site=site) if lang: frappe.local.lang = lang if session: # hack to enable access to private files in PDF session['data'] = frappe._dict(session['data']) frappe.local.session.update(session) # upto 3 retries for i in xrange(3): try: communication = frappe.get_doc("Communication", communication_name) communication._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc) except MySQLdb.OperationalError, e: # deadlock, try again if e.args[0] == 1213: frappe.db.rollback() time.sleep(1) continue else: raise else: break except: traceback = log( "frappe.tasks.sendmail", frappe.as_json({ "site": site, "communication_name": communication_name, "print_html": print_html, "print_format": print_format, "attachments": attachments, "recipients": recipients, "cc": cc, "lang": lang })) task_logger.warn(traceback) raise else: frappe.db.commit() finally: frappe.destroy()
def get_data(self): self.report_dict = { "gstin": "", "ret_period": "", "inward_sup": { "isup_details": [{ "ty": "GST", "intra": 0, "inter": 0 }, { "ty": "NONGST", "inter": 0, "intra": 0 }] }, "sup_details": { "osup_zero": { "csamt": 0, "txval": 0, "iamt": 0 }, "osup_nil_exmp": { "txval": 0 }, "osup_det": { "samt": 0, "csamt": 0, "txval": 0, "camt": 0, "iamt": 0 }, "isup_rev": { "samt": 0, "csamt": 0, "txval": 0, "camt": 0, "iamt": 0 }, "osup_nongst": { "txval": 0, } }, "inter_sup": { "unreg_details": [], "comp_details": [], "uin_details": [] }, "itc_elg": { "itc_avl": [{ "csamt": 0, "samt": 0, "ty": "IMPG", "camt": 0, "iamt": 0 }, { "csamt": 0, "samt": 0, "ty": "IMPS", "camt": 0, "iamt": 0 }, { "samt": 0, "csamt": 0, "ty": "ISRC", "camt": 0, "iamt": 0 }, { "ty": "ISD", "iamt": 0, "camt": 0, "samt": 0, "csamt": 0 }, { "samt": 0, "csamt": 0, "ty": "OTH", "camt": 0, "iamt": 0 }], "itc_rev": [{ "ty": "RUL", "iamt": 0, "camt": 0, "samt": 0, "csamt": 0 }, { "ty": "OTH", "iamt": 0, "camt": 0, "samt": 0, "csamt": 0 }], "itc_net": { "samt": 0, "csamt": 0, "camt": 0, "iamt": 0 }, "itc_inelg": [{ "ty": "RUL", "iamt": 0, "camt": 0, "samt": 0, "csamt": 0 }, { "ty": "OTH", "iamt": 0, "camt": 0, "samt": 0, "csamt": 0 }] } } self.gst_details = self.get_company_gst_details() self.report_dict["gstin"] = self.gst_details.get("gstin") self.report_dict["ret_period"] = get_period(self.month, self.year) self.month_no = get_period(self.month) self.account_heads = self.get_account_heads() outward_supply_tax_amounts = self.get_tax_amounts("Sales Invoice") inward_supply_tax_amounts = self.get_tax_amounts("Purchase Invoice", reverse_charge="Y") itc_details = self.get_itc_details() self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered", "Overseas"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt( self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) inter_state_supplies = self.get_inter_state_supplies( self.gst_details.get("gst_state_number")) inward_nil_exempt = self.get_inward_nil_exempt( self.gst_details.get("gst_state")) self.set_inter_state_supply(inter_state_supplies) self.set_inward_nil_exempt(inward_nil_exempt) self.missing_field_invoices = self.get_missing_field_invoices() self.json_output = frappe.as_json(self.report_dict)
def insert(self, doc): res = self.session.post(self.url + "/api/resource/" + doc.get("doctype"), data={"data": frappe.as_json(doc)}, verify=self.verify) return self.post_process(res)
def paypal_log(response, params=None): frappe.get_doc({ "doctype": "Paypal Log", "error": frappe.as_json(response), "params": frappe.as_json(params or "") }).insert(ignore_permissions=True)
def catchall(self, event): if event['type'] != 'worker-heartbeat': self.state.event(event) if not 'uuid' in event: return task = self.state.tasks.get(event['uuid']) info = task.info() if 'name' in event and 'enqueue_events_for_site' in event['name']: return try: kwargs = eval(info.get('kwargs')) if 'site' in kwargs: frappe.connect(kwargs['site']) if event['type']=='task-sent': make_async_task({ 'name': event['uuid'], 'task_name': kwargs.get("cmd") or event['name'] }) elif event['type']=='task-received': try: task = frappe.get_doc("Async Task", event['uuid']) task.status = 'Started' task.set_docstatus_user_and_timestamp() task.db_update() task.notify_update() except frappe.DoesNotExistError: pass elif event['type']=='task-succeeded': try: task = frappe.get_doc("Async Task", event['uuid']) task.status = 'Succeeded' task.result = info.get('result') task.runtime = info.get('runtime') task.set_docstatus_user_and_timestamp() task.db_update() task.notify_update() except frappe.DoesNotExistError: pass elif event['type']=='task-failed': try: task = frappe.get_doc("Async Task", event['uuid']) task.status = 'Failed' task.traceback = event.get('traceback') or event.get('exception') task.traceback = frappe.as_json(info) + "\n\n" + task.traceback task.runtime = info.get('runtime') task.set_docstatus_user_and_timestamp() task.db_update() task.notify_update() except frappe.DoesNotExistError: pass frappe.db.commit() except Exception: print frappe.get_traceback() finally: frappe.destroy()
def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" def get_seen(status): if not status: return None seen = 1 if status == "SEEN" else 0 return seen if self.enable_incoming: uid_list = [] exceptions = [] seen_status = [] uid_reindexed = False if frappe.local.flags.in_test: incoming_mails = test_mails else: email_sync_rule = self.build_email_sync_rule() email_server = None try: email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) except Exception: frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) if not email_server: return emails = email_server.get_messages() if not emails: return incoming_mails = emails.get("latest_messages", []) uid_list = emails.get("uid_list", []) seen_status = emails.get("seen_status", []) uid_reindexed = emails.get("uid_reindexed", False) for idx, msg in enumerate(incoming_mails): uid = None if not uid_list else uid_list[idx] try: args = { "uid": uid, "seen": None if not seen_status else get_seen(seen_status.get(uid, None)), "uid_reindexed": uid_reindexed } communication = self.insert_communication(msg, args=args) except SentEmailInInbox: frappe.db.rollback() except Exception: frappe.db.rollback() log('email_account.receive') if self.use_imap: self.handle_bad_emails(email_server, uid, msg, frappe.get_traceback()) exceptions.append(frappe.get_traceback()) else: frappe.db.commit() if communication: attachments = [d.file_name for d in communication._attachments] communication.notify(attachments=attachments, fetched_from_email_account=True) #notify if user is linked to account if len(incoming_mails)>0 and not frappe.local.flags.in_test: frappe.publish_realtime('new_email', {"account":self.email_account_name, "number":len(incoming_mails)}) if exceptions: raise Exception(frappe.as_json(exceptions))
def get_context(context): if frappe.session.user == "Guest": frappe.throw(_("Log in to access this page."), frappe.PermissionError) elif frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User": frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) hooks = frappe.get_hooks() try: boot = frappe.sessions.get() except Exception as e: boot = frappe._dict(status="failed", error=str(e)) print(frappe.get_traceback()) # this needs commit csrf_token = frappe.sessions.get_csrf_token() frappe.db.commit() desk_theme = frappe.db.get_value("User", frappe.session.user, "desk_theme") boot_json = frappe.as_json(boot) # remove script tags from boot boot_json = re.sub(r"\<script[^<]*\</script\>", "", boot_json) # TODO: Find better fix boot_json = re.sub(r"</script\>", "", boot_json) style_urls = hooks["app_include_css"] context.update({ "no_cache": 1, "build_version": frappe.utils.get_build_version(), "include_js": hooks["app_include_js"], "include_css": get_rtl_styles(style_urls) if is_rtl() else style_urls, "layout_direction": "rtl" if is_rtl() else "ltr", "lang": frappe.local.lang, "sounds": hooks["sounds"], "boot": boot if context.get("for_mobile") else boot_json, "desk_theme": desk_theme or "Light", "csrf_token": csrf_token, "google_analytics_id": frappe.conf.get("google_analytics_id"), "google_analytics_anonymize_ip": frappe.conf.get("google_analytics_anonymize_ip"), "mixpanel_id": frappe.conf.get("mixpanel_id"), }) return context
def load_translations(self, context): translated_messages = frappe.translate.get_dict( 'doctype', self.doc_type) # Sr is not added by default, had to be added manually translated_messages['Sr'] = _('Sr') context.translated_messages = frappe.as_json(translated_messages)
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False): from erpbee.accounts.doctype.pricing_rule.utils import ( get_pricing_rules, get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule) if isinstance(doc, string_types): doc = json.loads(doc) if doc: doc = frappe.get_doc(doc) if (args.get('is_free_item') or args.get("parenttype") == "Material Request"): return {} item_details = frappe._dict({ "doctype": args.doctype, "has_margin": False, "name": args.name, "parent": args.parent, "parenttype": args.parenttype, "child_docname": args.get('child_docname') }) if args.ignore_pricing_rule or not args.item_code: if frappe.db.exists(args.doctype, args.name) and args.get("pricing_rules"): item_details = remove_pricing_rule_for_item( args.get("pricing_rules"), item_details, args.get('item_code')) return item_details update_args_for_pricing_rule(args) pricing_rules = (get_applied_pricing_rules(args.get('pricing_rules')) if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc)) if pricing_rules: rules = [] for pricing_rule in pricing_rules: if not pricing_rule: continue if isinstance(pricing_rule, string_types): pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) pricing_rule.apply_rule_on_other_items = get_pricing_rule_items( pricing_rule) if pricing_rule.get('suggestion'): continue item_details.validate_applied_rule = pricing_rule.get( "validate_applied_rule", 0) item_details.price_or_product_discount = pricing_rule.get( "price_or_product_discount") rules.append(get_pricing_rule_details(args, pricing_rule)) if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other: item_details.update({ 'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items), 'price_or_product_discount': pricing_rule.price_or_product_discount, 'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other) if pricing_rule.apply_rule_on_other else frappe.scrub( pricing_rule.get('apply_on'))) }) if pricing_rule.coupon_code_based == 1 and args.coupon_code == None: return item_details if not pricing_rule.validate_applied_rule: if pricing_rule.price_or_product_discount == "Price": apply_price_discount_rule(pricing_rule, item_details, args) else: get_product_discount_rule(pricing_rule, item_details, args, doc) if not item_details.get("has_margin"): item_details.margin_type = None item_details.margin_rate_or_amount = 0.0 item_details.has_pricing_rule = 1 item_details.pricing_rules = frappe.as_json( [d.pricing_rule for d in rules]) if not doc: return item_details elif args.get("pricing_rules"): item_details = remove_pricing_rule_for_item(args.get("pricing_rules"), item_details, args.get('item_code')) return item_details
def get_context(context): frappe.errprint(frappe.session.user) if (frappe.session.user == "Guest" or frappe.db.get_value( "User", frappe.session.user, "user_type") == "Website User"): frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) hooks = frappe.get_hooks() try: boot = frappe.sessions.get() except Exception as e: boot = frappe._dict(status='failed', error=str(e)) print(frappe.get_traceback()) # this needs commit csrf_token = frappe.sessions.get_csrf_token() frappe.db.commit() boot_json = frappe.as_json(boot) # remove script tags from boot boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json) user_name = frappe.get_value( "User", frappe.session.user, "full_name" ) if frappe.session.user != "Administrator" else "Lewin Villar" physician = 'PHY-00006' if frappe.session.user == "Administrator" else get_physician_id( frappe.session.user) fact_fiscal = get_fiscal_invoices(physician) context.update({ "no_cache": 1, "build_version": get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], "usr": frappe.session.user, "usr_name": user_name, "sounds": hooks["sounds"], "boot": boot if context.get("for_mobile") else boot_json, "csrf_token": csrf_token, "background_image": (boot.status != 'failed' and (boot.user.background_image or boot.default_background_image) or None), "google_analytics_id": frappe.conf.get("google_analytics_id"), "mixpanel_id": frappe.conf.get("mixpanel_id"), "facturas_fiscal": fact_fiscal, "total_fact_fiscal": sum(i.grand_total for i in fact_fiscal), "paid_fact_fiscal": sum(i.paid_amount for i in fact_fiscal), })
def test_sync_field_order(self): from frappe.modules.import_file import get_file_path import os # create test doctype test_doctype = frappe.get_doc({ "doctype": "DocType", "module": "Core", "fields": [{ "label": "Field 1", "fieldname": "field_1", "fieldtype": "Data" }, { "label": "Field 2", "fieldname": "field_2", "fieldtype": "Data" }, { "label": "Field 3", "fieldname": "field_3", "fieldtype": "Data" }, { "label": "Field 4", "fieldname": "field_4", "fieldtype": "Data" }], "permissions": [{ "role": "System Manager", "read": 1 }], "name": "Test Field Order DocType", "__islocal": 1 }) path = get_file_path(test_doctype.module, test_doctype.doctype, test_doctype.name) initial_fields_order = ['field_1', 'field_2', 'field_3', 'field_4'] frappe.delete_doc_if_exists("DocType", "Test Field Order DocType") if os.path.isfile(path): os.remove(path) try: frappe.flags.allow_doctype_export = 1 test_doctype.save() # assert that field_order list is being created with the default order test_doctype_json = frappe.get_file_json(path) self.assertTrue(test_doctype_json.get("field_order")) self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order'])) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order']) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order) self.assertListEqual(test_doctype_json['field_order'], initial_fields_order) # remove field_order to test reload_doc/sync/migrate is backwards compatible without field_order del test_doctype_json['field_order'] with open(path, 'w+') as txtfile: txtfile.write(frappe.as_json(test_doctype_json)) # assert that field_order is actually removed from the json file test_doctype_json = frappe.get_file_json(path) self.assertFalse(test_doctype_json.get("field_order")) # make sure that migrate/sync is backwards compatible without field_order frappe.reload_doctype(test_doctype.name, force=True) test_doctype.reload() # assert that field_order list is being created with the default order again test_doctype.save() test_doctype_json = frappe.get_file_json(path) self.assertTrue(test_doctype_json.get("field_order")) self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order'])) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order']) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order) self.assertListEqual(test_doctype_json['field_order'], initial_fields_order) # reorder fields: swap row 1 and 3 test_doctype.fields[0], test_doctype.fields[ 2] = test_doctype.fields[2], test_doctype.fields[0] for i, f in enumerate(test_doctype.fields): f.idx = i + 1 # assert that reordering fields only affects `field_order` rather than `fields` attr test_doctype.save() test_doctype_json = frappe.get_file_json(path) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order) self.assertListEqual(test_doctype_json['field_order'], ['field_3', 'field_2', 'field_1', 'field_4']) # reorder `field_order` in the json file: swap row 2 and 4 test_doctype_json['field_order'][1], test_doctype_json[ 'field_order'][3] = test_doctype_json['field_order'][ 3], test_doctype_json['field_order'][1] with open(path, 'w+') as txtfile: txtfile.write(frappe.as_json(test_doctype_json)) # assert that reordering `field_order` from json file is reflected in DocType upon migrate/sync frappe.reload_doctype(test_doctype.name, force=True) test_doctype.reload() self.assertListEqual([f.fieldname for f in test_doctype.fields], ['field_3', 'field_4', 'field_1', 'field_2']) # insert row in the middle and remove first row (field 3) test_doctype.append("fields", { "label": "Field 5", "fieldname": "field_5", "fieldtype": "Data" }) test_doctype.fields[4], test_doctype.fields[ 3] = test_doctype.fields[3], test_doctype.fields[4] test_doctype.fields[3], test_doctype.fields[ 2] = test_doctype.fields[2], test_doctype.fields[3] test_doctype.remove(test_doctype.fields[0]) for i, f in enumerate(test_doctype.fields): f.idx = i + 1 test_doctype.save() test_doctype_json = frappe.get_file_json(path) self.assertListEqual( [f['fieldname'] for f in test_doctype_json['fields']], ['field_1', 'field_2', 'field_4', 'field_5']) self.assertListEqual(test_doctype_json['field_order'], ['field_4', 'field_5', 'field_1', 'field_2']) except: raise finally: frappe.flags.allow_doctype_export = 0
def sendmail(communication_name, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, bcc=None, lang=None, session=None, print_letterhead=None): try: if lang: frappe.local.lang = lang if session: # hack to enable access to private files in PDF session['data'] = frappe._dict(session['data']) frappe.local.session.update(session) if print_letterhead: frappe.flags.print_letterhead = print_letterhead # upto 3 retries for i in range(3): try: communication = frappe.get_doc("Communication", communication_name) communication._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc, bcc=bcc) except frappe.db.InternalError as e: # deadlock, try again if frappe.db.is_deadlocked(e): frappe.db.rollback() time.sleep(1) continue else: raise else: break except: traceback = log( "frappe.core.doctype.communication.email.sendmail", frappe.as_json({ "communication_name": communication_name, "print_html": print_html, "print_format": print_format, "attachments": attachments, "recipients": recipients, "cc": cc, "bcc": bcc, "lang": lang })) frappe.logger(__name__).error(traceback) raise
def get_website_settings(): hooks = frappe.get_hooks() context = frappe._dict({ 'top_bar_items': get_items('top_bar_items'), 'footer_items': get_items('footer_items'), "post_login": [{ "label": _("My Account"), "url": "/me" }, { "label": _("Logout"), "url": "/?cmd=web_logout" }] }) settings = frappe.get_single("Website Settings") for k in [ "banner_html", "brand_html", "copyright", "twitter_share_via", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup", "hide_footer_signup", "head_html", "title_prefix", "navbar_search" ]: if hasattr(settings, k): context[k] = settings.get(k) if settings.address: context["footer_address"] = settings.address for k in [ "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup" ]: context[k] = int(context.get(k) or 0) if frappe.request: context.url = quote(str(get_request_site_address(full_address=True)), safe="/:") context.encoded_title = quote(encode(context.title or ""), str("")) for update_website_context in hooks.update_website_context or []: frappe.get_attr(update_website_context)(context) context.web_include_js = hooks.web_include_js or [] translated_messages = {} translated_messages.update(frappe.translate.get_dict('template')) for file in context.web_include_js: translated_messages.update(frappe.translate.get_dict('jsfile', file)) context.translated_messages = frappe.as_json(translated_messages) context.web_include_css = hooks.web_include_css or [] via_hooks = frappe.get_hooks("website_context") for key in via_hooks: context[key] = via_hooks[key] if key not in ("top_bar_items", "footer_items", "post_login") \ and isinstance(context[key], (list, tuple)): context[key] = context[key][-1] add_website_theme(context) if not context.get("favicon"): context["favicon"] = "/assets/frappe/images/favicon.png" if settings.favicon and settings.favicon != "attach_files:": context["favicon"] = settings.favicon return context
def load_translations(self, context): translated_messages = frappe.translate.get_dict( 'doctype', self.doc_type) context.translated_messages = frappe.as_json(translated_messages)
def send_huawei_notifications(tokens=None, topic=None, title=None, body=None, data=None, custom_android_configuration=None): config = frappe.get_site_config().get("huawei_push_kit_config") if not config or not config.get('app_id') or not config.get( 'client_id') or not config.get('client_secret'): frappe.log_error(title="Huawei Push Kit Error", message="Message: {}".format( frappe._("Missing secret keys in config"))) return authorization_token = get_huawei_auth_token(config) if not authorization_token: frappe.log_error(title="Huawei Push Kit Error", message="Message: {}".format( frappe._("Authorization token missing."))) return url = "https://push-api.cloud.huawei.com/v1/{}/messages:send".format( config.get('app_id')) # message format # { # data:str , # notification: { 'title' , 'body' , 'image' }, # android: check docs.., # apns: check docs.., # webpush: check docs.., # token: [] , # topic: [] , # condition : '' check docs... # } message = { "data": frappe.as_json(data) if data else {}, "notification": { "title": title, "body": body }, "android": { "notification": { "click_action": { "type": 3 } } } } if custom_android_configuration and isinstance( custom_android_configuration, dict): message['android'].update(custom_android_configuration) response = None headers = { "Content-Type": "application/json", "Authorization": authorization_token } if tokens and len(tokens): message.update({"token": tokens}) try: payload = frappe._dict(validate_only=False, message=message) response = make_post_request(url, data=frappe.as_json(payload), headers=headers) huawei_push_kit_error_handler(tokens=tokens, topic=topic, title=title, body=body, data=data, recipient_count=len(tokens), request_params=message) except Exception as exc: huawei_push_kit_error_handler(tokens=tokens, topic=topic, title=title, body=body, data=data, exc=exc, recipient_count=len(tokens), request_params=message) print("Sending to tokens: {}".format(tokens)) elif topic: message.update({"topic": topic}) try: payload = frappe._dict(validate_only=False, message=message) response = make_post_request(url, data=frappe.as_json(payload), headers=headers) huawei_push_kit_error_handler(tokens=tokens, topic=topic, title=title, body=body, data=data, request_params=message) except Exception as exc: huawei_push_kit_error_handler(tokens=tokens, topic=topic, title=title, body=body, data=data, exc=exc, request_params=message) print("Sent TOPIC {} Msg: {}".format(topic, response)) return response
def as_json(self): return frappe.as_json(self.as_dict())
def set_value(doc, field, value): if isinstance(value, list): doc[field] = json.loads(frappe.as_json(value)) else: doc[field] = value
def submit(self, doclist): return self.post_request({ "cmd": "frappe.client.submit", "doclist": frappe.as_json(doclist) })
def put(self, path, data): return requests.put(f"{self.RESOURCE_URL}/{path}?sid={self.sid}", data=frappe.as_json(data))
def bulk_update(self, docs): return self.post_request({ "cmd": "frappe.client.bulk_update", "docs": frappe.as_json(docs) })
def run_action(action, context={}, kwargs={}): monitor, metrics = frappe.db.get_value( 'Action', action, ['allow_monitoring', 'allow_metrics']) if monitor: frappe.emit_js( "frappe.show_alert('Action {} started!')".format(action)) if metrics: start_time = now_datetime() original_context = context.copy() if not isinstance( context, six.string_types) else context original_kwargs = kwargs.copy() if not isinstance( kwargs, six.string_types) else kwargs if isinstance(context, six.string_types): context = json.loads(context) if context.get('doc'): doc = context.pop('doc') meta = frappe.get_meta(doc.get('doctype')) if hasattr(doc, 'as_json'): doc = json.loads(doc.as_json()) context['meta'] = json.loads(meta.as_json()) else: doc = {} if isinstance(kwargs, six.string_types): kwargs = json.loads(kwargs) jsi = JSInterpreter() jsi.evaljs(""" ctx = dukpy.context; kwargs = dukpy.kwargs; doc = dukpy.doc; delete dukpy; """, context=context, kwargs=kwargs, doc=doc) code = """try {{ {code} }} catch (e) {{ ctx.err = true; ctx.err_message = e.message; ctx.err_stack = e.stack; frappe.ui.msgprint([ frappe._("JS Engine Error:") + e.message ? " " + e.message : "", "<pre>" + e.stack + "</pre>" ].join("<br/>")); }}""".format(code=frappe.db.get_value('Action', action, 'code')) ret = jsi.evaljs(code) new_ctx, new_doc = jsi.evaljs('[ctx, doc];') if new_ctx.get('enabled_document_syncronization'): if not new_ctx.get("err", False): frappe.db.commit() else: if monitor: frappe.emit_js( "frappe.show_alert('Action {} failed!')".format(action)) if metrics: end_time = now_datetime() log_metrics(action, start_time, end_time, original_context, original_kwargs, err_message=new_ctx['err_message'], err_stack=new_ctx['err_stack']) raise Exception('Action: {0} - {1}\n{2}'.format( action, new_ctx['err_message'], new_ctx['err_stack'])) if isinstance(new_doc, six.string_types): new_doc = json.loads(new_doc) if monitor: frappe.emit_js( "frappe.show_alert('Action {} done!')".format(action)) if metrics: end_time = now_datetime() log_metrics(action, start_time, end_time, original_context, original_kwargs, result=frappe.as_json(new_doc)) if frappe.db.get_value("Action", action, "action_type") in ("User Action", "Event Action"): return {'docs': [json.loads(frappe.get_doc(new_doc).as_json())]} return new_doc, ret
def create_report_number_card(args): card = create_number_card(args) args = frappe.parse_json(args) args.name = card.name if args.dashboard: add_card_to_dashboard(frappe.as_json(args))