Ejemplo n.º 1
0
def make_dimension_in_accounting_doctypes(doc):
    doclist = get_doctypes_with_dimensions()
    doc_count = len(get_accounting_dimensions())
    count = 0

    for doctype in doclist:

        if (doc_count + 1) % 2 == 0:
            insert_after_field = 'dimension_col_break'
        else:
            insert_after_field = 'accounting_dimensions_section'

        df = {
            "fieldname": doc.fieldname,
            "label": doc.label,
            "fieldtype": "Link",
            "options": doc.document_type,
            "insert_after": insert_after_field,
            "owner": "Administrator"
        }

        if doctype == "Budget":
            add_dimension_to_budget_doctype(df, doc)
        else:
            meta = frappe.get_meta(doctype, cached=False)
            fieldnames = [d.fieldname for d in meta.get("fields")]

            if df['fieldname'] not in fieldnames:
                create_custom_field(doctype, df)

        count += 1

        frappe.publish_progress(count * 100 / len(doclist),
                                title=_("Creating Dimensions..."))
        frappe.clear_cache(doctype=doctype)
Ejemplo n.º 2
0
def assign_salary_structure_for_employees(employees,
                                          salary_structure,
                                          payroll_payable_account=None,
                                          from_date=None,
                                          base=None,
                                          variable=None,
                                          income_tax_slab=None):
    salary_structures_assignments = []
    existing_assignments_for = get_existing_assignments(
        employees, salary_structure, from_date)
    count = 0
    for employee in employees:
        if employee in existing_assignments_for:
            continue
        count += 1

        salary_structures_assignment = create_salary_structures_assignment(
            employee, salary_structure, payroll_payable_account, from_date,
            base, variable, income_tax_slab)
        salary_structures_assignments.append(salary_structures_assignment)
        frappe.publish_progress(
            count * 100 / len(set(employees) - set(existing_assignments_for)),
            title=_("Assigning Structures..."))

    if salary_structures_assignments:
        frappe.msgprint(_("Structures have been assigned successfully"))
Ejemplo n.º 3
0
def create_salary_slips_for_employees(employees, args, publish_progress=True):
    salary_slips_exists_for = get_existing_salary_slips(employees, args)
    count = 0
    for emp in employees:
        if emp not in salary_slips_exists_for:
            args.update({"doctype": "Salary Slip", "employee": emp})
            ss = frappe.get_doc(args)
            ss.insert()
            count += 1
            if publish_progress:
                frappe.publish_progress(
                    count * 100 /
                    len(set(employees) - set(salary_slips_exists_for)),
                    title=_("Creating Salary Slips..."))
        else:
            salary_slip_name = frappe.db.sql('''SELECT
						name
					FROM `tabSalary Slip`
					WHERE company=%s
					AND start_date >= %s
					AND end_date <= %s
					AND employee = %s
				''', (args.company, args.start_date, args.end_date, emp),
                                             as_dict=True)

            salary_slip_doc = frappe.get_doc('Salary Slip',
                                             salary_slip_name[0].name)
            salary_slip_doc.exchange_rate = args.exchange_rate
            salary_slip_doc.set_totals()
            salary_slip_doc.db_update()

    payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
    payroll_entry.db_set("salary_slips_created", 1)
    payroll_entry.notify_update()
Ejemplo n.º 4
0
def create_salary_slips_for_employees(employees, args, publish_progress=True):
    salary_slips_exists_for = get_existing_salary_slips(employees, args)
    count = 0
    salary_slips_not_created = []
    for emp in employees:
        if emp not in salary_slips_exists_for:
            args.update({"doctype": "Salary Slip", "employee": emp})
            ss = frappe.get_doc(args)
            ss.insert()
            count += 1
            if publish_progress:
                frappe.publish_progress(
                    count * 100 /
                    len(set(employees) - set(salary_slips_exists_for)),
                    title=_("Creating Salary Slips..."))

        else:
            salary_slips_not_created.append(emp)

    payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
    payroll_entry.db_set("salary_slips_created", 1)
    payroll_entry.notify_update()

    if salary_slips_not_created:
        frappe.msgprint(_(
            "Salary Slips already exists for employees {}, and will not be processed by this payroll."
        ).format(
            frappe.bold(", ".join([emp for emp in salary_slips_not_created]))),
                        title=_("Message"),
                        indicator="orange")
Ejemplo n.º 5
0
def create_bank_entries(columns, data, bank_account):
	bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
	bank = frappe.get_doc("Bank", bank_name)
	header_map = get_header_mapping(columns, bank)

	success = 0
	errors = 0
	total = len(json.loads(data))
	for idx, d in enumerate(json.loads(data)):
		if all(item is None for item in d) is True:
			continue
		fields = {}
		for key, value in iteritems(header_map):
			fields.update({
				key: get_value(d, key, bank, int(value)-1)
			})

		frappe.publish_progress(((idx+1)*100)/total, title=_("Importing Transactions"),description=_("Transaction {0} of {1}.").format(idx, total))

		try:
			bank_transaction = frappe.get_doc({
				"doctype": "Bank Transaction"
			})
			bank_transaction.update(fields)
			bank_transaction.date = getdate(parse_date(bank_transaction.date))
			bank_transaction.bank_account = bank_account
			bank_transaction.insert()
			bank_transaction.submit()
			success += 1
		except Exception:
			frappe.log_error(frappe.get_traceback())
			errors += 1

	return {"success": success, "errors": errors}
Ejemplo n.º 6
0
def make_dimension_in_accounting_doctypes(doc):
    doclist = get_doctypes_with_dimensions()
    doc_count = len(get_accounting_dimensions())
    count = 0

    for doctype in doclist:

        if (doc_count + 1) % 2 == 0:
            insert_after_field = 'dimension_col_break'
        else:
            insert_after_field = 'accounting_dimensions_section'

        df = {
            "fieldname": doc.fieldname,
            "label": doc.label,
            "fieldtype": "Link",
            "options": doc.document_type,
            "insert_after": insert_after_field
        }

        if frappe.db.exists("DocType", doctype):
            if doctype == "Budget":
                add_dimension_to_budget_doctype(df, doc)
            else:
                create_custom_field(doctype, df)

        count += 1

        frappe.publish_progress(count * 100 / len(doclist),
                                title=_("Creating Dimensions..."))
        frappe.clear_cache(doctype=doctype)
Ejemplo n.º 7
0
def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
	submitted_ss = []
	not_submitted_ss = []
	frappe.flags.via_payroll_entry = True

	count = 0
	for ss in salary_slips:
		ss_obj = frappe.get_doc("Salary Slip",ss[0])
		if ss_obj.net_pay<0:
			not_submitted_ss.append(ss[0])
		else:
			try:
				ss_obj.submit()
				submitted_ss.append(ss_obj)
			except frappe.ValidationError:
				not_submitted_ss.append(ss[0])

		count += 1
		if publish_progress:
			frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
	if submitted_ss:
		payroll_entry.make_accrual_jv_entry()
		frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
			.format(ss_obj.start_date, ss_obj.end_date))

		payroll_entry.email_salary_slip(submitted_ss)

		payroll_entry.db_set("salary_slips_submitted", 1)
		payroll_entry.notify_update()

	if not submitted_ss and not not_submitted_ss:
		frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))

	if not_submitted_ss:
		frappe.msgprint(_("Could not submit some Salary Slips"))
Ejemplo n.º 8
0
def update(doctype, field, value, condition='', limit=500):
	if not limit or cint(limit) > 500:
		limit = 500

	if condition:
		condition = ' where ' + condition

	if ';' in condition:
		frappe.throw(_('; not allowed in condition'))

	items = frappe.db.sql_list('''select name from `tab{0}`{1} limit 0, {2}'''.format(doctype,
		condition, limit), debug=1)
	n = len(items)

	for i, d in enumerate(items):
		doc = frappe.get_doc(doctype, d)
		doc.set(field, value)

		try:
			doc.save()
		except Exception as e:
			frappe.msgprint(_("Validation failed for {0}").format(frappe.bold(doc.name)))
			raise e

		frappe.publish_progress(float(i)*100/n,
			title = _('Updating Records'), doctype='Bulk Update', docname='Bulk Update')

	# clear messages
	frappe.local.message_log = []
	frappe.msgprint(_('{0} records updated').format(n), title=_('Success'), indicator='green')
Ejemplo n.º 9
0
def update(doctype, field, value, condition='', limit=500):
    if not limit or cint(limit) > 500:
        limit = 500

    if condition:
        condition = ' where ' + condition

    if ';' in condition:
        frappe.throw('; not allowed in condition')

    items = frappe.db.sql_list(
        '''select name from `tab{0}`{1} limit 0, {2}'''.format(
            doctype, condition, limit),
        debug=1)
    n = len(items)

    for i, d in enumerate(items):
        doc = frappe.get_doc(doctype, d)
        doc.set(field, value)
        doc.save()

        frappe.publish_progress(float(i) * 100 / n,
                                title=_('Updating Records'),
                                doctype='Bulk Update',
                                docname='Bulk Update')

    # clear messages
    frappe.local.message_log = []
    frappe.msgprint(_('{0} records updated').format(n),
                    title=_('Success'),
                    indicator='green')
Ejemplo n.º 10
0
 def import_orders_from_neos_server(self):
     NEOSConnect_settings = frappe.get_doc("NEOSConnect Settings")
     ftp = FTP(NEOSConnect_settings.ftp_host)
     ftp.login(NEOSConnect_settings.ftp_user, NEOSConnect_settings.ftp_pass)
     filenames = ftp.nlst()
     to_do_files = []
     for filename in filenames:
         if filename.startswith("order_") & filename.endswith(".csv"):
             to_do_files.append(filename)
     path = (frappe.utils.get_files_path(is_private=1)) + "/neosconnect"
     if not os.path.exists(path):
         os.makedirs(path)
     #gefundene Dateien Verarbeiten
     run_count = 0
     for to_do_file in to_do_files:
         run_count += 1
         percent = run_count * 100 / len(to_do_files)
         frappe.publish_progress(percent, "verarbeite Dateien")
         local_filename = path + "/" + to_do_file
         if os.path.isfile(local_filename):
             os.remove(local_filename)
         file_writer = open(local_filename, 'wb')
         ftp.retrbinary('RETR ' + to_do_file, file_writer.write)
         file_writer.close()
         ftp.delete(to_do_file)
         self.process_file(to_do_file, path, NEOSConnect_settings, True)
         os.remove(local_filename)
     ftp.quit()
     pass
Ejemplo n.º 11
0
def update(doctype, field, value, condition='', limit=500):
    if not limit or cint(limit) > 500:
        limit = 500

    if condition:
        condition = ' where ' + condition

    if ';' in condition:
        frappe.throw('; not allowed in condition')

    items = frappe.db.sql_list(
        '''select name from `tab{0}`{1} limit 0, {2}'''.format(
            doctype, condition, limit),
        debug=1)
    n = len(items)

    for i, d in enumerate(items):
        doc = frappe.get_doc(doctype, d)
        doc.set(field, value)

        try:
            doc.save()
        except Exception, e:
            frappe.msgprint(
                _("Validation failed for {0}").format(frappe.bold(doc.name)))
            raise e

        frappe.publish_progress(float(i) * 100 / n,
                                title=_('Updating Records'),
                                doctype='Bulk Update',
                                docname='Bulk Update')
Ejemplo n.º 12
0
def show_progress(docnames, message, i, description):
	n = len(docnames)
	if n >= 10:
		frappe.publish_progress(
			float(i) * 100 / n,
			title = message,
			description = description
		)
Ejemplo n.º 13
0
def show_progress(docnames, message, i, description):
	n = len(docnames)
	if n >= 5:
		frappe.publish_progress(
			float(i) * 100 / n,
			title = message,
			description = description
		)
Ejemplo n.º 14
0
def update_variants(variants, template, publish_progress=True):
    total = len(variants)
    for count, d in enumerate(variants, start=1):
        variant = frappe.get_doc("Item", d)
        copy_attributes_to_variant(template, variant)
        variant.save()
        if publish_progress:
            frappe.publish_progress(count / total * 100,
                                    title=_("Updating Variants..."))
Ejemplo n.º 15
0
def update_variants(variants, template, publish_progress=True):
	count=0
	for d in variants:
		variant = frappe.get_doc("Item", d)
		copy_attributes_to_variant(template, variant)
		variant.save()
		count+=1
		if publish_progress:
				frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
Ejemplo n.º 16
0
def update_variants(variants, template, publish_progress=True):
	count=0
	for d in variants:
		variant = frappe.get_doc("Item", d)
		copy_attributes_to_variant(template, variant)
		variant.save()
		count+=1
		if publish_progress:
				frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
Ejemplo n.º 17
0
    def set_delivery_note_for_tickets(self):
        settings = frappe.get_doc("OTRSConnect Settings")
        ERPNext_fetched_tickets = frappe.get_all("OTRSConnect Ticket",
                                                 filters={
                                                     "status": "fetched",
                                                     "erpnext_customer":
                                                     ("!=", "")
                                                 })
        print(len(ERPNext_fetched_tickets))
        run_count = 0
        for ticketname in ERPNext_fetched_tickets:
            percent = run_count * 100 / len(ERPNext_fetched_tickets)
            run_count += 1
            frappe.publish_progress(percent, "verarbeite Tickets")

            ticket_doc = frappe.get_doc("OTRSConnect Ticket", ticketname)
            delivery_notes = frappe.get_all("Delivery Note",
                                            filters={
                                                "title":
                                                settings.delivery_note_title,
                                                "customer":
                                                ticket_doc.erpnext_customer,
                                                "status": "Draft"
                                            })

            if len(delivery_notes) == 0:
                delivery_note_doc = frappe.get_doc({
                    "doctype":
                    "Delivery Note",
                    "customer":
                    ticket_doc.erpnext_customer,
                    "title":
                    settings.delivery_note_title,
                    "status":
                    "Draft",
                    "company":
                    frappe.get_doc("Global Defaults").default_company
                })
                for item in self.get_items_for_delivery_note_from_articles(
                        ticket_doc):
                    delivery_note_doc.append("items", item)
                delivery_note_doc.insert()
            else:
                delivery_note_doc = frappe.get_doc("Delivery Note",
                                                   delivery_notes[0])
                for item in self.get_items_for_delivery_note_from_articles(
                        ticket_doc):
                    delivery_note_doc.append("items", item)
                delivery_note_doc.save()
            ticket_doc.status = "delivered"
            ticket_doc.save()
            ticket_doc.submit()
        frappe.msgprint(str(run_count) + " Tickets verarbeitet.")
Ejemplo n.º 18
0
def cancel_all_linked_docs(docs):
	"""
	Cancel all linked doctype

	Arguments:
		docs (str) - It contains all list of dictionaries of a linked documents.
	"""

	docs = json.loads(docs)
	for i, doc in enumerate(docs, 1):
		if validate_linked_doc(doc) is True:
			frappe.publish_progress(percent=i * 100 / len(docs), title=_("Cancelling documents"))
			linked_doc = frappe.get_doc(doc.get("doctype"), doc.get("name"))
			linked_doc.cancel()
Ejemplo n.º 19
0
def execute(doctype, name, party, lang=None):
    """
    Queue calls this method, when it's ready.

    1. Create necessary folders
    2. Get raw PDF data
    3. Save PDF file and attach it to the document
    """
    settings = frappe.get_single("PDF on Submit Settings")
    show_progress = not settings.create_pdf_in_background
    progress_title = _("Creating PDF ...")

    if lang:
        frappe.local.lang = lang

    if show_progress:
        publish_progress(percent=0, title=progress_title)

    doctype_folder = create_folder(_(doctype), "Home")
    party_folder = create_folder(party, doctype_folder)

    if show_progress:
        publish_progress(percent=33, title=progress_title)

    pdf_data = get_pdf_data(doctype, name)

    if show_progress:
        publish_progress(percent=66, title=progress_title)

    save_and_attach(pdf_data, doctype, name, party_folder)

    if show_progress:
        publish_progress(percent=100, title=progress_title)
Ejemplo n.º 20
0
def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None):
	salary_structures_assignments = []
	existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date)
	count=0
	for employee in employees:
		if employee in existing_assignments_for:
			continue
		count +=1

		salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable)
		salary_structures_assignments.append(salary_structures_assignment)
		frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures..."))

	if salary_structures_assignments:
		frappe.msgprint(_("Structures have been assigned successfully"))
Ejemplo n.º 21
0
def create_log(initiative_count,
               initiative_target,
               month,
               args,
               publish_progress=True):
    if frappe.db.sql(
            """select count(name) from `tabBSC Initiative Log` where docstatus < 2  
	and month = %s and bsc_initiative = %s""",
        (month, args.bsc_initiative))[0][0] == 0:
        log_args = frappe._dict({
            "doctype": "BSC Initiative Log",
            "bsc_initiative": args.bsc_initiative,
            "bsc_target": args.bsc_target,
            "department": args.department,
            "fiscal_year": args.fiscal_year,
            "month": month,
            "log_target": initiative_target,
            "log_count": initiative_count,
            "employee": args.employee,
        })
        il = frappe.get_doc(log_args)
        il.insert()

    # create the BSC Ledger Entry#
    ble = frappe.get_doc(
        frappe._dict({
            "bsc_indicator": args.bsc_indicator,
            "bsc_target": args.bsc_target,
            "bsc_initiative": args.bsc_initiative,
            "entry_type": "Targeted",
            "month": month,
            "entry_number": initiative_target,
            "entry_count": initiative_count,
            "department": args.department,
            "fiscal_year": args.fiscal_year,
            "doctype": "BSC Ledger Entry"
        }))
    ble.insert()
    #

    if publish_progress:
        frappe.publish_progress(
            100,
            title=_("Creating BSC Initiative Log for {0}...").format(month))
    #bsc_initiative= frappe.get_doc("BSC Initiative", args.bsc_initiative)
    #bsc_initiative.db_set("initiative_logs_created", 1)
    #bsc_initiative.notify_update()
Ejemplo n.º 22
0
def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]):
	"""
	Cancel all linked doctype, optionally ignore doctypes specified in a list.

	Arguments:
		docs (json str) - It contains list of dictionaries of a linked documents.
		ignore_doctypes_on_cancel_all (list) - List of doctypes to ignore while cancelling.
	"""

	docs = json.loads(docs)
	if isinstance(ignore_doctypes_on_cancel_all, str):
		ignore_doctypes_on_cancel_all = json.loads(ignore_doctypes_on_cancel_all)
	for i, doc in enumerate(docs, 1):
		if validate_linked_doc(doc, ignore_doctypes_on_cancel_all):
			linked_doc = frappe.get_doc(doc.get("doctype"), doc.get("name"))
			linked_doc.cancel()
		frappe.publish_progress(percent=i/len(docs) * 100, title=_("Cancelling documents"))
Ejemplo n.º 23
0
def create_salary_slips_for_employees(employees, args, publish_progress=True):
    salary_slips_exists_for = get_existing_salary_slips(employees, args)
    count = 0
    for emp in employees:
        if emp not in salary_slips_exists_for:
            args.update({"doctype": "Salary Slip", "employee": emp})
            ss = frappe.get_doc(args)
            ss.insert()
            count += 1
            if publish_progress:
                frappe.publish_progress(
                    count * 100 /
                    len(set(employees) - set(salary_slips_exists_for)),
                    title=_("Creating Salary Slips..."))

    payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
    payroll_entry.db_set("salary_slips_created", 1)
    payroll_entry.notify_update()
Ejemplo n.º 24
0
    def import_from_csv_folder(self):
        NEOSConnect_settings = frappe.get_doc("NEOSConnect Settings")
        #CSV Headers, which we expect
        csv_headers = {}
        csv_headers['NEOS_note'] = [
            "map_id", "man_name", "man_aid", "desc_short", "price_min",
            "price_special", "qty_status_max", "item_remarks", "user_name",
            "sup_name", "sup_id", "sup_aid", "price_amount", "qty_status",
            "item_qty", "vk_netto"
        ]
        csv_headers['NEOS_order'] = [
            "map_id", "sup_name", "sup_id", "sup_aid", "man_name", "man_aid",
            "desc_short", "ean", "price_requested", "price_confirmed",
            "qty_requested", "qty_confirmed", "qty_delivered", "item_remark",
            "user_name", "reference", "customer_po", "order_name",
            "order_date", "response_date", "order_status"
        ]

        if NEOSConnect_settings.csv_import_folder != "":
            files = []
            run_count = 0
            files = os.listdir(NEOSConnect_settings.csv_import_folder)
            for file in files:
                run_count += 1
                percent = run_count * 100 / len(files)
                frappe.publish_progress(percent, "verarbeite Dateien")
                current_file = os.path.join(
                    NEOSConnect_settings.csv_import_folder, file)
                #NEOS Merkzettel
                if file.startswith("note_") & file.endswith(".csv"):
                    files.append(current_file)
                    self.process_csv(current_file, "NEOS_note",
                                     NEOSConnect_settings, csv_headers)
                #NEOS Bestellungen
                if file.startswith("order_") & file.endswith(".csv"):
                    files.append(current_file)
                    self.process_csv(current_file, "NEOS_order",
                                     NEOSConnect_settings, csv_headers)
            if len(files) == 0:
                frappe.throw("No files found in directory " +
                             NEOSConnect_settings.csv_import_folder)

        else:
            frappe.throw("CSV directory error")
Ejemplo n.º 25
0
 def set_ERPNext_OTRS_Tickets(self, closed_tickets_dict):
     run_count = 0
     for ticket in closed_tickets_dict:
         run_count += 1
         percent = run_count * 100 / len(closed_tickets_dict)
         frappe.publish_progress(percent, "verarbeite Tickets")
         ERPNext_tickets = frappe.get_all("OTRSConnect Ticket",
                                          filters={"id": ticket["id"]})
         if len(ERPNext_tickets) == 0:
             frappe_doctype_dict = {"doctype": "OTRSConnect Ticket"}
             ticket["id"] = str(ticket["id"])
             ticket["status"] = "fetched"
             frappe_doctype_dict.update(ticket)
             ticket_doc = frappe.get_doc(frappe_doctype_dict)
             inserted_ticket_doc = ticket_doc.insert()
             self.link_ERPNext_OTRS_Ticket(inserted_ticket_doc)
             self.set_ERPNext_OTRS_Articles(
                 self.get_Articles_for_Ticket_dict(inserted_ticket_doc))
     frappe.msgprint(str(run_count) + " Tickets verarbeitet.")
Ejemplo n.º 26
0
def create_salary_slips_for_employees(employees, args, publish_progress=True):
	salary_slips_exists_for = get_existing_salary_slips(employees, args)
	count=0
	for emp in employees:
		if emp not in salary_slips_exists_for:
			args.update({
				"doctype": "Salary Slip",
				"employee": emp
			})
			ss = frappe.get_doc(args)
			ss.insert()
			count+=1
			if publish_progress:
				frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
					title = _("Creating Salary Slips..."))

	payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
	payroll_entry.db_set("salary_slips_created", 1)
	payroll_entry.notify_update()
Ejemplo n.º 27
0
def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]):
    """
	Cancel all linked doctype

	Arguments:
		docs (str) - It contains all list of dictionaries of a linked documents.
	"""

    docs = json.loads(docs)
    if isinstance(ignore_doctypes_on_cancel_all, string_types):
        ignore_doctypes_on_cancel_all = json.loads(
            ignore_doctypes_on_cancel_all)
    for i, doc in enumerate(docs, 1):
        if validate_linked_doc(doc, ignore_doctypes_on_cancel_all) is True:
            frappe.publish_progress(
                percent=i * 100 /
                ((len(docs) - len(ignore_doctypes_on_cancel_all))),
                title=_("Cancelling documents"))
            linked_doc = frappe.get_doc(doc.get("doctype"), doc.get("name"))
            linked_doc.cancel()
Ejemplo n.º 28
0
def grant_leave_alloc_for_employees(employees, leave_period, carry_forward_leaves=0):
	leave_allocations = []
	existing_allocations_for = get_existing_allocations(employees, leave_period.name)
	leave_type_details = get_leave_type_details()
	count=0
	for employee in employees:
		if employee in existing_allocations_for:
			continue
		count +=1
		leave_policy = get_employee_leave_policy(employee)
		if leave_policy:
			for leave_policy_detail in leave_policy.leave_policy_details:
				if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp:
					leave_allocation = create_leave_allocation(employee, leave_policy_detail.leave_type,
						leave_policy_detail.annual_allocation, leave_type_details, leave_period, carry_forward_leaves)
					leave_allocations.append(leave_allocation)
		frappe.db.commit()
		frappe.publish_progress(count*100/len(set(employees) - set(existing_allocations_for)), title = _("Allocating leaves..."))

	if leave_allocations:
		frappe.msgprint(_("Leaves has been granted sucessfully"))
Ejemplo n.º 29
0
def create_salary_slips_and_payout(pp_list, args, publish_progress=True):
	#salary_slips_exists_for = get_existing_salary_slips(employees, args)
	count=0
	for pp_row in pp_list:
		#if emp not in salary_slips_exists_for:
		args.update({
			"doctype": "Salary Slip",
			"processed_payroll": pp_row["name"],
			"employee": pp_row["emp_id"]

		})
		ss = frappe.get_doc(args)
		ss.insert()
		ss.submit()
		count+=1
		processed_payroll = frappe.get_doc("Processed Payroll", pp_row["name"])
		processed_payroll.db_set("pay_slip_status", 'Paid')
		processed_payroll.notify_update()

		if publish_progress:
			frappe.publish_progress(count * 100 / len(pp_list),title=_("Creating Salary Slips. It may take few minutes."))
Ejemplo n.º 30
0
def grant_leave_alloc_for_employees(employee_records, leave_period, carry_forward=0):
	leave_allocations = []
	existing_allocations_for = get_existing_allocations(list(employee_records.keys()), leave_period.name)
	leave_type_details = get_leave_type_details()
	count = 0
	for employee in employee_records.keys():
		if employee in existing_allocations_for:
			continue
		count +=1
		leave_policy = get_employee_leave_policy(employee)
		if leave_policy:
			for leave_policy_detail in leave_policy.leave_policy_details:
				if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp:
					leave_allocation = create_leave_allocation(employee, leave_policy_detail.leave_type,
						leave_policy_detail.annual_allocation, leave_type_details, leave_period, carry_forward, employee_records.get(employee))
					leave_allocations.append(leave_allocation)
		frappe.db.commit()
		frappe.publish_progress(count*100/len(set(employee_records.keys()) - set(existing_allocations_for)), title = _("Allocating leaves..."))

	if leave_allocations:
		frappe.msgprint(_("Leaves has been granted sucessfully"))
Ejemplo n.º 31
0
def create_targets_for_departments(dep_list, args, publish_progress=True):
    targets_exists_for = get_existing_targets(dep_list, args)
    count = 0
    for dep in dep_list:
        if dep not in targets_exists_for:
            args.update({"doctype": "BSC Target", "department": dep})
            ss = frappe.get_doc(args)
            ss.insert()
            count += 1
            if publish_progress:
                frappe.publish_progress(
                    count * 100 / len(set(dep_list) - set(targets_exists_for)),
                    title=_("Creating BSC Target for {0} Department..."
                            ).format(dep))

    bsc_indicator = frappe.get_doc("BSC Indicator", args.bsc_indicator)
    bsc_indicator.db_set("create_count", bsc_indicator.create_count + 1)
    bsc_indicator.notify_update()
    if targets_exists_for:
        frappe.msgprint(
            _("Aleardy exist with {0}").format(', '.join(
                [str(dep) for dep in targets_exists_for])))
Ejemplo n.º 32
0
def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
	submitted_ss = []
	not_submitted_ss = []
	frappe.flags.via_payroll_entry = True

	count = 0
	for ss in salary_slips:
		ss_obj = frappe.get_doc("Salary Slip",ss[0])
		if ss_obj.net_pay<0:
			not_submitted_ss.append(ss[0])
		else:
			try:
				ss_obj.submit()
				submitted_ss.append(ss_obj)
			except frappe.ValidationError:
				not_submitted_ss.append(ss[0])

		count += 1
		if publish_progress:
			frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))

	if submitted_ss:
		payroll_entry.make_accrual_jv_entry()
		frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
			.format(ss_obj.start_date, ss_obj.end_date))

		payroll_entry.email_salary_slip(submitted_ss)

	payroll_entry.db_set("salary_slips_submitted", 1)
	payroll_entry.notify_update()

	if not submitted_ss and not not_submitted_ss:
		frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))

	if not_submitted_ss:
		frappe.msgprint(_("Could not submit some Salary Slips"))
def send_statements(company=None, manual=None):
    """
	Send out customer statements
	"""
    show_progress = manual
    progress_title = _("Sending customer statements...")

    if show_progress:
        publish_progress(percent=0, title=progress_title)

    if company is None:
        company = frappe.db.get_single_value('Customer Statements Sender',
                                             'company')
        if not company:
            frappe.throw(
                _('Company field is required on Customer Statements Sender'))
            exit()

    email_list = get_recipient_list()
    idx = 0
    total = len(email_list)
    for row in email_list:
        idx += 1
        if row.email_id is not None:
            if row.send_statement == "Yes":
                if show_progress:
                    publish_progress(
                        percent=(idx / total * 100),
                        title=progress_title,
                        description=' Creating PDF for {0}'.format(
                            row.customer))
                data = get_report_content(company, row.customer)
                # Get PDF Data
                pdf_data = get_pdf(data)
                if not pdf_data:
                    return

                attachments = [{
                    'fname': get_file_name(),
                    'fcontent': pdf_data
                }]

                frappe.sendmail(
                    recipients=row.email_id,
                    subject='Customer Statement from {0}'.format(company),
                    message=
                    'Good day. <br> Please find attached your latest statement from {0}'
                    .format(company),
                    attachments=attachments,
                    reference_doctype="Report",
                    reference_name="General Ledger")

    if show_progress:
        publish_progress(percent=100, title=progress_title)
        frappe.msgprint('Emails queued for sending')
def send_statements(company=None, manual=None):
    """
    Send out customer statements
    """
    show_progress = manual
    progress_title = _("Sending customer statements...")

    if show_progress:
        publish_progress(percent=0, title=progress_title)

    if company is None:
        company = frappe.db.get_single_value("Customer Statements Sender",
                                             "company")
        if not company:
            frappe.throw(
                _("Company field is required on Customer Statements Sender"))
            exit()

    from_date_for_all_customers = frappe.db.get_single_value(
        "Customer Statements Sender", "from_date_for_all_customers")
    to_date_for_all_customers = frappe.db.get_single_value(
        "Customer Statements Sender", "to_date_for_all_customers")

    email_list = get_recipient_list()
    idx = 0
    total = len(email_list)
    for row in email_list:
        idx += 1
        if row.email_id is not None and row.email_id != "":
            if row.send_statement == "Yes":
                if show_progress:
                    publish_progress(
                        percent=(idx / total * 100),
                        title=progress_title,
                        description=" Creating PDF for {0}".format(
                            row.customer),
                    )
                send_individual_statement(
                    row.customer,
                    row.email_id,
                    company,
                    from_date_for_all_customers,
                    to_date_for_all_customers,
                )

    if show_progress:
        publish_progress(percent=100, title=progress_title)
        frappe.msgprint("Emails queued for sending")
Ejemplo n.º 35
0
def submit_salary_slips_for_employees_mod(payroll_entry,
                                          salary_slips,
                                          publish_progress=True):
    """
		MODIFIED: Single line changed
		payroll_entry.make_accrual_jv_entry() -> payroll_entry.register_payroll_in_gl(cancel=False)
	"""
    submitted_ss = []
    not_submitted_ss = []
    frappe.flags.via_payroll_entry = True

    count = 0
    for ss in salary_slips:
        ss_obj = frappe.get_doc("Salary Slip", ss[0])
        if ss_obj.net_pay < 0:
            not_submitted_ss.append(ss[0])
        else:
            try:
                ss_obj.submit()
                submitted_ss.append(ss_obj)
            except frappe.ValidationError:
                not_submitted_ss.append(ss[0])

        count += 1
        if publish_progress:
            frappe.publish_progress(count * 100 / len(salary_slips),
                                    title=_("Submitting Salary Slips..."))

    if submitted_ss:
        #payroll_entry.make_accrual_jv_entry()
        payroll_entry.register_payroll_in_gl(cancel=False)
        frappe.msgprint(
            _("Salary Slip submitted for period from {0} to {1}").format(
                ss_obj.start_date, ss_obj.end_date))

        payroll_entry.email_salary_slip(submitted_ss)

    payroll_entry.db_set("salary_slips_submitted", 1)
    payroll_entry.notify_update()

    if not submitted_ss and not not_submitted_ss:
        frappe.msgprint(
            _("No salary slip found to submit for the above selected criteria OR salary slip already submitted"
              ))

    if not_submitted_ss:
        frappe.msgprint(_("Could not submit some Salary Slips"))

#########################################################################
### NOTHING TO SEE BELOW HERE; OLD STUFF KEPT FOR REVIEW IF NECESSARY ###
#########################################################################

# def something_else(self):

# 	payable_amounts = {}
# 	total_payable = 0
# 	for slip in slips:
# 		name = slip['salary_slip']
# 		emp = slip['employee']
# 		net_amount = frappe.db.get_value(doctype="Salary Slip", fieldname="rounded_total", filters={"name": name})
# 		payable_amounts[emp] = net_amount
# 		total_payable += net_amount

# 	self.set('base_grand_total', total_payable)

# 	# register accounts that will be set against earnings and deductions
# 	against_earnings = []
# 	for acct in deductions:
# 		against_earnings.append(acct)
# 	if payroll_account_is_type_payable:
# 		for emp in payable_amounts:
# 			against_earnings.append(emp)
# 	else:
# 		against_earnings.append(default_payroll_payable_account)

# 	against_deductions = []
# for acct in earnings:
# 	against_deductions.append(acct)

# 	### Now we'll build up the general ledger map
# 	gl_map = []
# 	if self.aggregate_salary_slips:
# 		if earnings or deductions:
# 		for acc, amount in earnings.items():
# 			print(acc, amount)
# 			gl_map.append(self.new_gl_line(
# 				account=acc,
# 				against=", ".join(list(set(against_earnings))),
# 				debit=amount
# 			))

# 		# deductions
# 		for acc, amount in deductions.items():
# 			gl_map.append(self.new_gl_line(
# 				account=acc,
# 				against=", ".join(list(set(against_deductions))),
# 				credit=amount,
# 			))

# 		# Loan
# 		for loan in loan_details:
# 			gl_map.append(self.new_gl_line(
# 				account=loan.loan_account,
# 				against=loan.employee,
# 				credit=loan.principal_amount,
# 				party_type="Employee",
# 				party=loan.employee
# 			))

# 			if loan.interest_amount and not loan.interest_income_account:
# 					frappe.throw(_("Select interest income account in employee loan {0}").format(loan.loan))

# 				if loan.interest_income_account and loan.interest_amount:
# 					gl_map.append(self.new_gl_line(
# 						account=loan.interest_income_account,
# 						against=loan.employee,
# 						credit=loan.interest_amount,

# 					))

# 		gl_map.append(self.new_gl_line(
# 			account=default_payroll_payable_account,
# 			against=",".join(list(set(against_deductions))),
# 			credit=total_payable
# 		))

# 	else:
# 		# if payroll_account_is_type_payable:
# 		# 	#for emp, amt in payable_amounts.items():
# 		# #	gl_map.append(self.new_gl_line(
# 		# #		account=default_payroll_payable_account,
# 		# #		against=", ".join(list(set(against_deductions))),
# 		# #		credit=amt,
# 		# #		party_type="Employee",
# 		# #		party=emp
# 		# #	))
# 	for slip in slips:
# 			name = slip['salary_slip']
# 			#slip_doc = frappe.get_doc("Salary Slip", name)
# 			emp = slip['employee']
# 			net_amount = frappe.db.get_value(doctype="Salary Slip", fieldname="rounded_total", filters={"name": name})
# 			payable_amounts[emp] = net_amount
# 			total_payable += net_amount
# 			print(slip)
# 			gl_map.append(self.new_gl_line(
# 			account=default_payroll_payable_account,
# 			against=", ".join(list(set(against_deductions))),
# 			credit=net_amount,
# 			party_type="Employee",
# 			party=emp,
# 			against_voucher=name,
# 			against_voucher_type="Salary Slip"
# 		))

# 	# earnings and deductions
# if earnings or deductions:
# 	# earnings
# 	for acc, amount in earnings.items():
# 		print(acc, amount)
# 		gl_map.append(self.new_gl_line(
# 			account=acc,
# 			against=", ".join(list(set(against_earnings))),
# 			debit=amount
# 		))

# 	# deductions
# 	for acc, amount in deductions.items():
# 		gl_map.append(self.new_gl_line(
# 			account=acc,
# 			against=", ".join(list(set(against_deductions))),
# 			credit=amount,
# 		))

# 	# Loan

# 		###
# 		##Get rid of this distinction; this entire section will now be for aggregated
# 		###

# 		# payable
# 	if payroll_account_is_type_payable:
# 		for slip in slips:
#  			name = slip['salary_slip']
#  			#slip_doc = frappe.get_doc("Salary Slip", name)
#  			emp = slip['employee']
#  			net_amount = frappe.db.get_value(doctype="Salary Slip", fieldname="rounded_total", filters={"name": name})
#  			payable_amounts[emp] = net_amount
#  			total_payable += net_amount
#  			print(slip)
#  			gl_map.append(self.new_gl_line(
# 				account=default_payroll_payable_account,
# 				against=", ".join(list(set(against_deductions))),
# 				credit=net_amount,
# 				party_type="Employee",
# 				party=emp,
# 				against_voucher=name,
# 				against_voucher_type="Salary Slip"
# 			))

# 		#for emp, amt in payable_amounts.items():
# 		#	gl_map.append(self.new_gl_line(
# 		#		account=default_payroll_payable_account,
# 		#		against=", ".join(list(set(against_deductions))),
# 		#		credit=amt,
# 		#		party_type="Employee",
# 		#		party=emp
# 		#	))

# 	else:
# 		gl_map.append(self.new_gl_line(
# 			account=default_payroll_payable_account,
# 			against=",".join(list(set(against_deductions))),
# 			credit=total_payable
# 		))
    """
		what follows are all functions used to fetch and aggregate various parts of Salary Slips, called
		by the register_payroll_in_gl function.
	"""
    # def get_salary_component_total(self, component_type = None):
    # 	salary_components = self.get_salary_components(component_type)
    # 	if salary_components:
    # 		component_dict = {}
    # 		for item in salary_components:
    # 			component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
    # 		account_details = self.get_account(component_dict = component_dict)
    # 		return account_details

    # def get_salary_components(self, component_type):
    # 	salary_slips = self.get_sal_slip_list(as_dict = True)
    # 	if salary_slips:
    # 		salary_components = frappe.db.sql("""select salary_component, amount, parentfield
    # 			from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
    # 			(component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.salary_slip for d in salary_slips]), as_dict=True)
    # 		return salary_components

    # def get_account(self, component_dict = None):
    # 	account_dict = {}
    # 	for s, a in component_dict.items():
    # 		account = self.get_salary_component_account(s)
    # 		account_dict[account] = account_dict.get(account, 0) + a
    # 	return account_dict

    # def get_salary_component_account(self, salary_component):
    # 	account = frappe.db.get_value("Salary Component Account",
    # 		{"parent": salary_component, "company": self.company}, "default_account")
    # 	if not account:
    # 		frappe.throw(_("Please set default account in Salary Component {0}")
    # 			.format(salary_component))
    # 	return account

    # def get_default_payroll_payable_account(self):
    # 	payroll_payable_account = frappe.db.get_value("Company",
    # 		{"company_name": self.company}, "default_payroll_payable_account")
    # 	if not payroll_payable_account:
    # 		frappe.throw(_("Please set Default Payroll Payable Account in Company {0}")
    # 			.format(self.company))
    # 	return payroll_payable_account

    # def get_loan_details(self):
    # 	"""
    # 		Get loan details from submitted salary slip based on selected criteria
    # 	"""
    # 	cond = self.get_filter_condition()
    # 	return frappe.db.sql(""" select t1.employee, eld.loan_account, eld.loan,
    # 			eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
    # 		from
    # 			`tabSalary Slip` t1, `tabSalary Slip Loan` eld
    # 		where
    # 			t1.docstatus = 1 and t1.name = eld.parent and start_date >= %s and end_date <= %s %s
    # 		""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True) or []

    # def get_total_salary_amount(self):
    # 	"""
    # 		Get total salary amount from submitted salary slip based on selected criteria
    # 	"""
    # 	cond = self.get_filter_condition()
    # 	totals = frappe.db.sql(""" select sum(rounded_total) as rounded_total from `tabSalary Slip` t1
    # 		where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
    # 		""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True)
    # 	return totals and totals[0] or None


# 	def update_salary_slip_status(self, jv_name = None):
# 		ss_list = self.get_sal_slip_list(ss_status=1)
# 		for ss in ss_list:
# 			ss_obj = frappe.get_doc("Salary Slip",ss[0])
# 			frappe.db.set_value("Salary Slip", ss_obj.name, "status", "Paid")
# 			frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)

# 	def set_start_end_dates(self):
# 		self.update(get_start_end_dates(self.payroll_frequency,
# 			self.start_date or self.posting_date, self.company))

# @frappe.whitelist()
# def get_start_end_dates(payroll_frequency, start_date=None, company=None):
# 	'''Returns dict of start and end dates for given payroll frequency based on start_date'''

# 	if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly" or payroll_frequency == "":
# 		fiscal_year = get_fiscal_year(start_date, company=company)[0]
# 		month = "%02d" % getdate(start_date).month
# 		m = get_month_details(fiscal_year, month)
# 		if payroll_frequency == "Bimonthly":
# 			if getdate(start_date).day <= 15:
# 				start_date = m['month_start_date']
# 				end_date = m['month_mid_end_date']
# 			else:
# 				start_date = m['month_mid_start_date']
# 				end_date = m['month_end_date']
# 		else:
# 			start_date = m['month_start_date']
# 			end_date = m['month_end_date']

# 	if payroll_frequency == "Weekly":
# 		end_date = add_days(start_date, 6)

# 	if payroll_frequency == "Fortnightly":
# 		end_date = add_days(start_date, 13)

# 	if payroll_frequency == "Daily":
# 		end_date = start_date

# 	return frappe._dict({
# 		'start_date': start_date, 'end_date': end_date
# 	})

# def validate_employee_attendance(self):
# 	employees_to_mark_attendance = []
# 	days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
# 	for employee_detail in self.employees:
# 		days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
# 		days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
# 		days_in_payroll = date_diff(self.end_date, self.start_date) + 1
# 		if days_in_payroll > days_holiday + days_attendance_marked:
# 			employees_to_mark_attendance.append({
# 				"employee": employee_detail.employee,
# 				"employee_name": employee_detail.employee_name
# 				})
# 	return employees_to_mark_attendance

# def get_count_holidays_of_employee(self, employee):
# 	holiday_list = get_holiday_list_for_employee(employee)
# 	holidays = 0
# 	if holiday_list:
# 		days = frappe.db.sql("""select count(*) from tabHoliday where
# 			parent=%s and holiday_date between %s and %s""", (holiday_list,
# 			self.start_date, self.end_date))
# 		if days and days[0][0]:
# 			holidays = days[0][0]
# 	return holidays

# def get_count_employee_attendance(self, employee):
# 	marked_days = 0
# 	attendances = frappe.db.sql("""select count(*) from tabAttendance where
# 		employee=%s and docstatus=1 and attendance_date between %s and %s""",
# 		(employee, self.start_date, self.end_date))
# 	if attendances and attendances[0][0]:
# 		marked_days = attendances[0][0]
# 	return marked_days

# def get_frequency_kwargs(frequency_name):
# 	frequency_dict = {
# 		'monthly': {'months': 1},
# 		'fortnightly': {'days': 14},
# 		'weekly': {'days': 7},
# 		'daily': {'days': 1}
# 	}
# 	return frequency_dict.get(frequency_name)

# @frappe.whitelist()
# def get_end_date(start_date, frequency):
# 	start_date = getdate(start_date)
# 	frequency = frequency.lower() if frequency else 'monthly'
# 	kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')

# 	# weekly, fortnightly and daily intervals have fixed days so no problems
# 	end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
# 	if frequency != 'bimonthly':
# 		return dict(end_date=end_date.strftime(DATE_FORMAT))

# 	else:
# 		return dict(end_date='')

# def get_month_details(year, month):
# 	ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
# 	if ysd:
# 		import calendar, datetime
# 		diff_mnt = cint(month)-cint(ysd.month)
# 		if diff_mnt<0:
# 			diff_mnt = 12-int(ysd.month)+cint(month)
# 		msd = ysd + relativedelta(months=diff_mnt) # month start date
# 		month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
# 		mid_start = datetime.date(msd.year, cint(month), 16) # month mid start date
# 		mid_end = datetime.date(msd.year, cint(month), 15) # month mid end date
# 		med = datetime.date(msd.year, cint(month), month_days) # month end date
# 		return frappe._dict({
# 			'year': msd.year,
# 			'month_start_date': msd,
# 			'month_end_date': med,
# 			'month_mid_start_date': mid_start,
# 			'month_mid_end_date': mid_end,
# 			'month_days': month_days
# 		})
# 	else:
# 		frappe.throw(_("Fiscal Year {0} not found").format(year))

# @frappe.whitelist()
# def create_log(ss_list):
# 	if not ss_list:
# 		frappe.throw(
# 			_("There are no employees for the listed criteria currently missing salary slips."),
# 			title='Note'
# 		)
# 	return ss_list

# def create_submit_log(submitted_ss, unsubmitted_ss):
# 	if not submitted_ss and not unsubmitted_ss:
# 		frappe.msgprint(_("No salary slips found for the above criteria"))

# 	if unsubmitted_ss:
# 		frappe.msgprint(_("Could not submit a Salary Slip <br>\
# 			Possible reasons: <br>\
# 			1. Net pay is less than 0. <br>\
# 			2. Company Email Address specified in employee master is not valid. <br>"))

# def format_as_links(salary_slip):
# 	return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]

# def get_salary_slip_list(name, docstatus, as_dict=0):
# 	payroll_entry = frappe.get_doc('Payroll Entry', name)

# 	salary_slip_list = frappe.db.sql(
# 		"select t1.name, t1.salary_structure from `tabSalary Slip` t1 "
# 		"where t1.docstatus = %s "
# 		"and t1.start_date >= %s "
# 		"and t1.end_date <= %s",
# 		(docstatus, payroll_entry.start_date, payroll_entry.end_date),
# 		as_dict=as_dict
# 	)

# 	return salary_slip_list

# @frappe.whitelist()
# def payroll_entry_has_created_slips(name):
# 	response = {}

# 	draft_salary_slips = get_salary_slip_list(name, docstatus=0)
# 	submitted_salary_slips = get_salary_slip_list(name, docstatus=1)

# 	response['draft'] = 1 if draft_salary_slips else 0
# 	response['submitted'] = 1 if submitted_salary_slips else 0

# 	return response

# def get_payroll_entry_bank_entries(payroll_entry_name):
# 	journal_entries = frappe.db.sql(
# 		'select name from `tabJournal Entry Account` '
# 		'where reference_type="Payroll Entry" '
# 		'and reference_name=%s and docstatus=1',
# 		payroll_entry_name,
# 		as_dict=1
# 	)

# 	return journal_entries

# @frappe.whitelist()
# def payroll_entry_has_bank_entries(name):
# 	response = {}

# 	bank_entries = get_payroll_entry_bank_entries(name)
# 	response['submitted'] = 1 if bank_entries else 0

# 	return response

###### CURRENTLY UNUSED METHODS FROM THE CORE CLASS
# def on_submit(self):
# 	# identical to payroll_entry
# 		self.submit_salary_slips()

# 	def before_submit(self):
# 		# identical to payroll entry
# 		if self.validate_attendance:
# 		if self.validate_employee_attendance():
# 			frappe.throw(_("Cannot Submit, Employees left to mark attendance"))

# def get_emp_list(self):
# 	# identical to payroll_entry method
# 	"""
# 		Returns list of active employees based on selected criteria
# 		and for which salary structure exists
# 	"""
# 	cond = self.get_filter_condition()
# 	cond += self.get_joining_releiving_condition()

# 	condition = ''
# 	if self.payroll_frequency:
# 		condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}

# 	sal_struct = frappe.db.sql_list("""
# 			select
# 				name from `tabSalary Structure`
# 			where
# 				docstatus = 1 and
# 				is_active = 'Yes'
# 				and company = %(company)s and
# 				ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
# 				{condition}""".format(condition=condition),
# 			{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
# 	if sal_struct:
# 		cond += "and t2.salary_structure IN %(sal_struct)s "
# 		cond += "and %(from_date)s >= t2.from_date"
# 		emp_list = frappe.db.sql("""
# 			select
# 				distinct t1.name as employee, t1.employee_name, t1.department, t1.designation
# 			from
# 				`tabEmployee` t1, `tabSalary Structure Assignment` t2
# 			where
# 				t1.name = t2.employee
# 				and t2.docstatus = 1
# 		%s order by t2.from_date desc
# 		""" % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date}, as_dict=True)
# 		return emp_list

# def fill_employee_details(self):
# # this method now unused and replaced with a fill_salary_slips
# 	self.set('employees', [])
# 	employees = self.get_emp_list()
# 	if not employees:
# 		frappe.throw(_("No employees for the mentioned criteria"))

# 	for d in employees:
# 		self.append('employees', d)

# 	self.number_of_employees = len(employees)
# 	if self.validate_attendance:
# 		return self.validate_employee_attendance()

# 	def get_joining_releiving_condition(self):
# 		# identical to payroll_entry method
# cond = """
# 	and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
# 	and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s'
# """ % {"start_date": self.start_date, "end_date": self.end_date}
# return cond

# def get_filter_condition(self):
# 	"""
# 		Assemble sql clause matching filters specified in the document
# 	"""
# 	for fieldname in ['company', 'start_date', 'end_date']:
# 		if not self.get(fieldname):
# 			frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))

# 	cond = ''
# 	for f in ['company', 'branch', 'department', 'designation']:
# 		if self.get(f):
# 			cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'"
# 	return cond

# def get_filter_condition(self):
# 	# identical to payment_entry
# 	self.check_mandatory()

# 	cond = ''
# 	for f in ['company', 'branch', 'department', 'designation']:
# 		if self.get(f):
# 			cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'"

# 	return cond

# def submit_salary_slips(self):
# 	"""
# 		Submit all salary slips listed in the Payroll Salary Slip Details table. This is typically done when the
# 		Payroll Voucher document is submitted.

# 		TODO: this could probably be simplified to simply iterate through the Payroll Salary Slip Details table
# 	"""

# 	self.check_permission('write')

# 	ss_list = self.get_sal_slip_list()
# 	submitted_ss = []
# 	unsubmitted_ss = []
# 	for ss in ss_list:
# 		ss_obj = frappe.get_doc("Salary Slip",ss[0])
# 		ss_dict = {}
# 		ss_dict["Employee Name"] = ss_obj.employee_name
# 		ss_dict["Total Pay"] = fmt_money(ss_obj.net_pay,
# 			currency = frappe.defaults.get_global_default("currency"))
# 		ss_dict["Salary Slip"] = format_as_links(ss_obj.name)[0]

# 		if ss_obj.net_pay<0:
# 			unsubmitted_ss.append(ss_dict)
# 		else:
# 			try:
# 				ss_obj.submit()
# 				submitted_ss.append(ss_obj)
# 			except frappe.ValidationError:
# 				unsubmitted_ss.append(ss_dict)

# 	if submitted_ss:
# 		self.register_payroll_in_gl()
# 		frappe.msgprint(_("Salary Slips submitted for period from {0} to {1}").format(ss_obj.start_date, ss_obj.end_date))
# 		self.email_salary_slip(submitted_ss)

# 	return create_submit_log(submitted_ss, unsubmitted_ss)

# def email_salary_slip(self, submitted_ss):
# 	"""
# 		send emails to employees if indicated by HR Settings
# 	"""
# 	if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"):
# 		for ss in submitted_ss:
# 			ss.email_salary_slip()
Ejemplo n.º 36
0
def upload_calendar(data):
    #Check if called from client side (not necessary)
    if (isinstance(data, str)):
        data = json.loads(data)

    #Connect to CalDav Account
    account = frappe.get_doc("CalDav Account", data["caldavaccount"])
    """
    account = data["caldavaccount"]
    """
    client = caldav.DAVClient(url=account.url,
                              username=account.username,
                              password=account.password)
    principal = client.principal()
    calendars = principal.calendars()
    cal = None

    #Look for the right calendar
    for calendar in calendars:
        if (str(calendar) == data["calendarurl"]):
            cal = calendar

    #Go through events
    erp_events = frappe.db.sql("""
        SELECT
            *
        FROM `tabEvent`
        WHERE icalendar = '{icalendar}' AND custom_pattern is NULL;
    """.format(icalendar=data["icalendar"]),
                               as_dict=1)

    weekdays = [
        "monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
        "sunday"
    ]
    rrweekdays = [RR.MO, RR.TU, RR.WE, RR.TH, RR.FR, RR.SA, RR.SU]

    upstats = {
        "10a": 0,
        "10b": 0,
        "10c": 0,
        "11b": 0,
        "not_uploadable": 0,
        "cancelled_or_closed_of_no_uploadable": 0,
        "exceptions": 0,
    }

    rstats = {
        "mapped": 0,
        "not_mapped": 0,
    }
    #Error Stack
    error_stack = []

    for idx, ee in enumerate(erp_events):
        uploadable = True
        try:
            print(ee)
            #Case 10a: Status Open, everything nominal
            if (ee["status"] == "Open"):
                new_calendar = vobject.newFromBehavior('vcalendar')
                e = new_calendar.add('vevent')
                e.add('summary').value = ee["subject"]
                dtstart = ee["starts_on"]
                e.add('dtstart').value = dtstart
                e.add('description').value = ee["description"]
                if (ee["event_type"] in ["Public", "Private", "Confidential"]):
                    e.add('class').value = ee["event_type"]
                #Case 10b: Status Open, but Event Type is Cancelled
                elif (ee["event_type"] == "Cancelled"):
                    uploadable = False
                    upstats["10b"] += 1
                #Case 10c: Status Open, but Event Type not in [Public, Private, Confidential,Cancelled]
                else:
                    uploadable = False
                    upstats["10c"] += 1
                    raise Exception(
                        'Exception:', 'Event with Name ' + ee["name"] +
                        ' has the invalid Event Type ' + ee["event_type"])
                dtend = ee["ends_on"]
                if (dtend == None):
                    dtend = dtstart + datetime.timedelta(minutes=15)
                    frappe.db.set_value('Event',
                                        ee["name"],
                                        'ends_on',
                                        dtend,
                                        update_modified=False)
                if (ee["all_day"] == 0):
                    e.add('dtend').value = dtend
                else:
                    e.dtstart.value = dtstart.date()
                    dtend = (dtend.date() + datetime.timedelta(days=1))
                    e.add('dtend').value = dtend

                if (ee["last_modified"] == None):
                    frappe.db.set_value('Event',
                                        ee["name"],
                                        'last_modified',
                                        ee["modified"].replace(microsecond=0),
                                        update_modified=False)
                    e.add('last-modified').value = ee["modified"].replace(
                        microsecond=0)
                else:
                    e.add('last-modified').value = ee["last_modified"]

                if (ee["created_on"] == None):
                    frappe.db.set_value('Event',
                                        ee["name"],
                                        'created_on',
                                        ee["creation"].replace(microsecond=0),
                                        update_modified=False)
                    e.add('created').value = ee["creation"].replace(
                        microsecond=0)
                else:
                    e.add('created').value = ee["created_on"]

                #Create rrule
                rrule = None
                until = ee["repeat_till"]
                byweekday = []
                if (ee["repeat_this_event"] == 1
                        and ee["repeat_till"] != None):
                    until = datetime.datetime(until.year, until.month,
                                              until.day, dtstart.hour,
                                              dtstart.minute, dtstart.second)
                if (ee["repeat_on"] == "Daily"):
                    rrule = RR.rrule(freq=RR.DAILY, until=until)
                elif (ee["repeat_on"] == "Weekly"):
                    for idx, weekday in enumerate(weekdays):
                        if (ee[weekday] == 1):
                            byweekday.append(rrweekdays[idx])
                    rrule = RR.rrule(freq=RR.WEEKLY,
                                     until=until,
                                     byweekday=byweekday)
                elif (ee["repeat_on"] == "Monthly"):
                    rrule = RR.rrule(freq=RR.MONTHLY, until=until)
                elif (ee["repeat_on"] == "Yearly"):
                    rrule = RR.rrule(freq=RR.YEARLY, until=until)

                if (rrule != None):
                    e.add('rrule').value = rrule
                    rstats["mapped"] += 1
                else:
                    rstats["not_mapped"] += 1

                #Remove None Children
                none_attributes = []
                for child in e.getChildren():
                    if (child.value == None):
                        none_attributes.append(child.name.lower())
                for attr in none_attributes:
                    e.__delattr__(attr)

                ics = new_calendar.serialize()
                print(ics)
                frappe.db.set_value('Event',
                                    ee["name"],
                                    'uid',
                                    e.uid.value,
                                    update_modified=False)

                #Upload
                if (uploadable):
                    cal.save_event(ics)
                    upstats["10a"] += 1
                else:
                    upstats["not_uploadable"] += 1
            #Case 11a: Status != Open
            else:
                uploadable = False
                upstats["11b"] += 1
        except Exception as ex:
            #traceback.print_exc()
            tb = traceback.format_exc()
            upstats["exceptions"] += 1
            error_stack.append({
                "message":
                "Could not upload event. Exception: \n" + tb,
                "event":
                json.dumps(ee)
            })

        #Update UI
        percent_progress = idx / len(erp_events) * 100
        frappe.publish_progress(percent=percent_progress, title="Uploading")

    #Return JSON and Log
    message = {}
    upstats["cancelled_or_closed_of_no_uploadable"] = upstats[
        "not_uploadable"] - upstats["10b"] - upstats["11b"]
    message["upstats"] = upstats
    message["rstats"] = rstats
    message["error_stack"] = error_stack
    """
    """

    d = frappe.get_doc("iCalendar", data["icalendar"])
    d.last_sync_log = json.dumps(message)
    d.save()
    d.add_comment('Comment',
                  text="Stats:\n" + str(upstats) + "\nRRule Stats:\n" +
                  str(rstats))
    """
    """

    return json.dumps(message)
Ejemplo n.º 37
0
def download_calendar(data):
    #UI Progress Display
    percent_progress = 0
    frappe.show_progress('Downloading..', percent_progress, 100, 'Please wait')

    #Constants
    sync_period = 90

    #Check if called from client side (not necessary)
    if (isinstance(data, str)):
        data = json.loads(data)

    #Connect to CalDav Account
    account = frappe.get_doc("CalDav Account", data["caldavaccount"])
    """
    account = data["caldavaccount"]
    """
    client = caldav.DAVClient(url=account.url,
                              username=account.username,
                              password=account.password)
    principal = client.principal()
    calendars = principal.calendars()
    cal = None
    doc = None

    #Look for the right calendar
    for calendar in calendars:
        if (str(calendar) == data["calendarurl"]):
            cal = calendar

    #Go through Events
    events = cal.events()

    #Stats
    stats = {
        "1a": 0,
        "1b": 0,
        "1c": 0,
        "2a": 0,
        "3a": 0,
        "3b": 0,
        "4a": 0,
        "else": 0,
        "error": 0,
        "not_inserted": 0,
        "exception_block_standard": 0,
        "exception_block_meta": 0
    }
    rstats = {
        "norrule": 0,
        "daily": 0,
        "weekly": 0,
        "monthly": 0,
        "yearly": 0,
        "finite": 0,
        "infinite": 0,
        "total": len(events),
        "singular_event": 0,
        "error": 0,
        "exception": 0
    }
    #Error Stack
    error_stack = []

    for idx, event in enumerate(events):
        vev = event.vobject_instance.vevent

        #By default an event is not insertable in ERP
        insertable = False

        #Handle standard fields
        try:
            #Following conversion makes it Timezone naive!
            if (hasattr(vev, "dtstart")
                    and type(vev.dtstart.value) is datetime.date):
                value = vev.dtstart.value
                dtstart = datetime.datetime(value.year, value.month, value.day)
            elif (hasattr(vev, "dtstart")):
                dtstart = vev.dtstart.value
            else:
                dtstart = None

            if (hasattr(vev, "dtend")
                    and type(vev.dtend.value) is datetime.date):
                value = vev.dtend.value
                dtend = datetime.datetime(value.year, value.month, value.day)
            elif (hasattr(vev, "dtend")):
                dtend = vev.dtend.value
            else:
                dtend = None

            #Berechne Dinge
            if (hasattr(vev, "dtstart") and hasattr(vev, "dtend")):
                timedelta = dtend - dtstart
                days = (dtend.date() - dtstart.date()).days
            elif (hasattr(vev, "dtstart") and hasattr(vev, "duration")):
                timedelta = vev.duration.value  #Real time difference
                days = (
                    (dtstart + vev.duration.value).date() -
                    dtstart.date()).days  #Full days between the dates 0,1,2...

            #Standard Fields
            doc = frappe.new_doc("Event")
            """
            doc = DotMap()
            """
            if (vev.summary.value == "WG"):
                vev.prettyPrint()

            doc.subject = vev.summary.value
            doc.starts_on = dtstart.strftime("%Y-%m-%d %H:%M:%S")
            if (hasattr(vev, "description")):
                doc.description = vev.description.value
            doc.event_type = vev.__getattr__("class").value.title()

            #Case 1a: has dtend, within a day
            days = None
            timedelta = None
            if ((hasattr(vev, "dtend") and days == 0)):
                doc.ends_on = dtend.strftime("%Y-%m-%d %H:%M:%S")
                insertable = True
                stats["1a"] += 1
            #Case 1b: has duration, within a day
            elif (hasattr(vev, "duration") and days == 0):
                doc.ends_on = (
                    dtstart + vev.duration.value).strftime("%Y-%m-%d %H:%M:%S")
                insertable = True
                stats["1b"] += 1
            #Case 1c: Allday, one day
            elif (timedelta.days == 1 and timedelta.seconds == 0
                  and dtstart.hour == 0 and dtstart.minute == 0):
                doc.ends_on = ""
                doc.all_day = 1
                insertable = True
                stats["1c"] += 1
            #Case 2a: Allday, more than one day
            elif (timedelta.days >= 1 and timedelta.seconds == 0):
                doc.ends_on = (dtstart +
                               timedelta).strftime("%Y-%m-%d %H:%M:%S")
                doc.all_day = 1
                insertable = True
                stats["2a"] += 1
            #Case 3a: has dtend, not within a day
            elif ((hasattr(vev, "dtend") and days >= 1)):
                doc.ends_on = (dtstart +
                               timedelta).strftime("%Y-%m-%d %H:%M:%S")
                insertable = True
                stats["3a"] += 1
            #Case 3b: has duration, not within a day
            elif ((hasattr(vev, "duration") and days > 0)):
                doc.ends_on = (dtstart +
                               timedelta).strftime("%Y-%m-%d %H:%M:%S")
                insertable = True
                stats["3b"] += 1
            #Case else: ( ATM: No dtend, No Duration,...)
            else:
                stats["else"] += 1

        except Exception as ex:
            #traceback.print_exc()
            tb = traceback.format_exc()
            insertable = False
            stats["exception_block_standard"] += 1
            error_stack.append({
                "message":
                "Problem with Standard Fields/Cases. Exception: \n" + tb,
                "icalendar":
                vev.serialize()
            })

        #If the event has a recurrence rule this will be handled here, by default rrules are not mappable to ERP
        mapped = False
        try:
            #RRULE CONVERSION
            if (hasattr(vev, "rrule")):
                rule = dateutil.rrule.rrulestr(vev.rrule.value,
                                               dtstart=vev.dtstart.value)

                #Include only mappable rrules
                if (isMappable(vev, rule)):
                    #DAILY
                    if (rule._freq == 3 and noByDay(vev.rrule.value)):
                        doc.repeat_this_event = 1
                        doc.repeat_on = "Daily"
                        until = getUntil(vev.dtstart.value, rule)
                        if until:
                            doc.repeat_till = until.strftime("%Y-%m-%d")
                        mapped = True
                        rstats["daily"] += 1
                    #DAILY to WEEKLY (Special Case SP1)
                    elif (rule._freq == 3 and not noByDay(vev.rrule.value)):
                        match = re.search(
                            r'BY[A-Z]{4,5}DAY', vev.rrule.value
                        )  #Catches BYWEEKDAY, BYMONTHDAY and BYYEARDAY
                        if match:
                            print("Special Case not applicable")
                            rstats["error"] += 1
                            error_stack.append({
                                "message": "Daily SP1 not applicable",
                                "icalendar": vev.serialize()
                            })
                        else:
                            doc.repeat_this_event = 1
                            doc.repeat_on = "Weekly"
                            until = getUntil(vev.dtstart.value, rule)
                            if until:
                                doc.repeat_till = until.strftime("%Y-%m-%d")
                            if 0 in rule._byweekday:
                                doc.monday = 1
                            if 1 in rule._byweekday:
                                doc.tuesday = 1
                            if 2 in rule._byweekday:
                                doc.wednesday = 1
                            if 3 in rule._byweekday:
                                doc.thursday = 1
                            if 4 in rule._byweekday:
                                doc.friday = 1
                            if 5 in rule._byweekday:
                                doc.saturday = 1
                            if 6 in rule._byweekday:
                                doc.sunday = 1
                            mapped = True
                            rstats["weekly"] += 1
                    #WEEKLY
                    elif (rule._freq == 2):
                        doc.repeat_this_event = 1
                        doc.repeat_on = "Weekly"
                        until = getUntil(vev.dtstart.value, rule)
                        if until:
                            doc.repeat_till = until.strftime("%Y-%m-%d")
                        if 0 in rule._byweekday:
                            doc.monday = 1
                        if 1 in rule._byweekday:
                            doc.tuesday = 1
                        if 2 in rule._byweekday:
                            doc.wednesday = 1
                        if 3 in rule._byweekday:
                            doc.thursday = 1
                        if 4 in rule._byweekday:
                            doc.friday = 1
                        if 5 in rule._byweekday:
                            doc.saturday = 1
                        if 6 in rule._byweekday:
                            doc.sunday = 1
                        mapped = True
                        rstats["weekly"] += 1
                    #MONTHLY
                    elif (rule._freq == 1 and noByDay(vev.rrule.value) and
                          isNotFebruaryException(vev.dtstart.value.date())):
                        doc.repeat_this_event = 1
                        doc.repeat_on = "Monthly"
                        until = getUntil(vev.dtstart.value, rule)
                        if until:
                            doc.repeat_till = until.strftime("%Y-%m-%d")
                        mapped = True
                        rstats["monthly"] += 1
                    #YEARLY
                    elif (rule._freq == 0 and noByDay(vev.rrule.value) and
                          isNotFebruaryException(vev.dtstart.value.date())):
                        doc.repeat_this_event = 1
                        doc.repeat_on = "Yearly"
                        until = getUntil(vev.dtstart.value, rule)
                        if until:
                            doc.repeat_till = until.strftime("%Y-%m-%d")
                        mapped = True
                        rstats["yearly"] += 1
                    #Not mapped
                    else:
                        rstats["error"] += 1
                        error_stack.append({
                            "message": "Mappable but Not mapped",
                            "icalendar": vev.serialize()
                        })

            else:
                mapped = True
                rstats["norrule"] += 1

        except Exception as ex:
            #traceback.print_exc()
            tb = traceback.format_exc()
            mapped = False
            rstats["exception"] += 1
            error_stack.append({
                "message":
                "RRule mapping error. Exception: \n" + tb,
                "icalendar":
                vev.serialize()
            })

        #Handle meta fields and insert doc into erp
        try:
            #Specials: Metafields
            if (hasattr(vev, "transp")):
                if (vev.transp.value == "TRANSPARENT"):
                    doc.color = color_variant(data["color"])
                elif (vev.transp.value == "OPAQUE"):
                    doc.color = data["color"]
            else:
                doc.color = data["color"]

            if (hasattr(vev, "status")):
                #print("Status: " + vev.status.value)
                pass
            if (hasattr(vev, "organizer")):
                #print("Organizer: " + vev.organizer.value)
                pass
            if (hasattr(vev, "attendee")):
                #vev.prettyPrint()
                pass
            if (hasattr(vev, "sequence")):
                #print("Sequence: " + vev.sequence.value)
                pass
            if (hasattr(vev, "location")):
                #print("Location: " + vev.location.value)
                pass

            #ICalendar Meta Information for Sync
            if (hasattr(vev, "last_modified")):
                doc.last_modified = vev.last_modified.value.strftime(
                    "%Y-%m-%d %H:%M:%S")
            if (hasattr(vev, "created")):
                doc.created_on = vev.created.value.strftime(
                    "%Y-%m-%d %H:%M:%S")
            if (hasattr(vev, "uid")):
                doc.uid = vev.uid.value
            else:
                raise Exception('Exception:', 'Event has no UID')
            doc.icalendar = data["icalendar"]

            #Insert
            if (insertable and mapped):
                """
                """
                doc.insert(
                    ignore_permissions=
                    False,  # ignore write permissions during insert
                    ignore_links=True,  # ignore Link validation in the document
                    ignore_if_duplicate=
                    True,  # dont insert if DuplicateEntryError is thrown
                    ignore_mandatory=
                    False  # insert even if mandatory fields are not set
                )
            #Has rrule, not mappable to Custom Pattern
            elif (hasattr(vev, "rrule")):
                #Finite Event
                is_finite = False
                if (re.search(r'UNTIL|COUNT', vev.rrule.value)):
                    #vev.prettyPrint()
                    datetimes = list(vev.getrruleset())
                    is_finite = True
                    rstats["finite"] += 1
                #Infinite Event
                else:
                    #vev.prettyPrint()
                    vev.rrule.value = vev.rrule.value + ";UNTIL=" + (
                        datetime.datetime.now() + datetime.timedelta(
                            days=sync_period)).strftime("%Y%m%d")
                    datetimes = list(vev.getrruleset())
                    rstats["infinite"] += 1

                #Does not need a Custom Pattern
                if (len(datetimes) == 1):
                    """
                    """
                    doc.insert(
                    )  #TODO Remeber this is a strange case too, cause it has a rrule but only one event
                    rstats["singular_event"] += 1
                #Create Custom Pattern and Linked Events
                else:
                    """
                    cp = DotMap()
                    """
                    cp = frappe.new_doc("Custom Pattern")
                    cp.title = vev.summary.value
                    cp.icalendar = data["icalendar"]
                    if (hasattr(vev, "created")):
                        cp.created_on = vev.created.value.strftime(
                            "%Y-%m-%d %H:%M:%S")
                    if (hasattr(vev, "last_modified")):
                        cp.last_modified = vev.last_modified.value.strftime(
                            "%Y-%m-%d %H:%M:%S")
                    if (hasattr(vev, "uid")):
                        cp.uid = vev.uid.value
                    else:
                        raise Exception("Exception:", "Event has no UID")
                    if (is_finite):
                        cp.duration = "Finite"
                    else:
                        cp.duration = "Infinite"

                    cp.newest_event_on = datetimes[
                        len(datetimes) - 1].strftime("%Y-%m-%d %H:%M:%S")
                    """
                    """
                    cp.insert()

                    for dt_starts_on in datetimes:
                        """
                        event = DotMap()
                        """
                        event = frappe.new_doc("Event")
                        event.subject = vev.summary.value
                        event.starts_on = dt_starts_on.strftime(
                            "%Y-%m-%d %H:%M:%S")
                        #In ERP Allday Events should have an empty field for ends_on -> dtendtime = None
                        if (doc.ends_on == "" or doc.ends_on is None):
                            event.ends_on = ""
                        else:
                            dtendtime = datetime.datetime.strptime(
                                doc.ends_on, "%Y-%m-%d %H:%M:%S").time()
                            event.ends_on = dt_starts_on.strftime(
                                "%Y-%m-%d") + " " + dtendtime.strftime(
                                    "%H:%M:%S")
                        event.type = vev.__getattr__("class").value.title()
                        if (hasattr(vev, "transp")):
                            if (vev.transp.value == "TRANSPARENT"):
                                event.color = color_variant(data["color"])
                            elif (vev.transp.value == "OPAQUE"):
                                event.color = data["color"]
                        else:
                            event.color = data["color"]

                        if (hasattr(vev, "description")):
                            event.description = vev.description.value
                        else:
                            event.description = None

                        event.repeat_this_event = 1
                        """
                        """
                        event.custom_pattern = cp.name
                        event.insert()
                        cp.append('events', {'event': event.name})
                    cp.save()
                    """
                    """

                    #print(datetimes)
                    #print("CP created.")

            #Has no rrule, not mappable and strange for unknown reason
            else:
                stats["not_inserted"] += 1

        except Exception as ex:
            #traceback.print_exc()
            tb = traceback.format_exc()
            stats["exception_block_meta"] += 1
            error_stack.append({
                "message":
                "Problem with Meta fields or doc insertion. Exception: \n" +
                tb,
                "icalendar":
                vev.serialize()
            })

        #Update UI
        percent_progress = idx / len(events) * 100
        frappe.publish_progress(percent=percent_progress, title="Downloading")

    #Return JSON and Log
    message = {}
    message["stats"] = stats
    message["rstats"] = rstats
    message["error_stack"] = error_stack
    """
    """

    d = frappe.get_doc("iCalendar", data["icalendar"])
    d.last_sync_log = json.dumps(message)
    d.save()
    d.add_comment('Comment',
                  text="Stats:\n" + str(stats) + "\nRRule Stats:\n" +
                  str(rstats))
    """
    """

    return json.dumps(message)