def get_wage(self, project=None): """Get wage.""" # tr to get projects wage if check_objects.is_project(project): p_wage = project.get_wage() else: p_wage = Decimal(0) # return project wage, if own wage is 0, other wise return own wage if self._wage == 0: return p_wage else: return self._wage
def get_finish_days(self, project=None): """Calculate and return the finish days.""" # if no project is given, return -1 if not check_objects.is_project(project): return -1 # get time needed for this offer time = self.get_time_total() # get hours per day hours_per_day = project.get_hours_per_day() # get days and add the minimum days finish_days = round( time.full() / hours_per_day) + project.get_minimum_days() # return calculation return finish_days
def get_finish_date_and_days(self, project=None): """Calculate and return the finish date and finish days.""" # if no project is given, return 1987-15-10 if not check_objects.is_project(project) or self._date is None: return ddate(1987, 10, 15), 0 # get time needed for this offer time = self.get_time_total() # get initial date date = self._date # initialize finish days output days = 0 # get first workday while date.weekday() not in project.get_work_days(): days += 1 date += timedelta(days=1) # add minimum_days min_days = project.get_minimum_days() while min_days > 0: # add the day only, if it is a working day if date.weekday() in project.get_work_days(): min_days -= 1 # add a day days += 1 date += timedelta(days=1) # subtract hours_per_day from time on work_days, till time <= 0 while time > 0: # t's a work day so subtract hours_per_day from time if date.weekday() in project.get_work_days(): time -= project.get_hours_per_day() # add a day days += 1 date += timedelta(days=1) return date, days
def generate_parameter( single_account='', payee='', settings=None, project=None, invoice=None ): """Generate parameter for ledgeradd according to given invoice.""" is_settings = check_objects.is_settings(settings) is_project = check_objects.is_project(project) is_invoice = check_objects.is_invoice(invoice) if not is_settings or not is_project or not is_invoice: return False # cancel if there are no entries if len(invoice.get_entry_list()) == 0: return False # get all the data args = [] # the date and state if invoice.get_paid_date() is None: args.append('-d "{}"'.format(invoice.get_date().strftime('%Y-%m-%d'))) args.append('-aux "{}"'.format(invoice.get_date().strftime('%Y-%m-%d'))) args.append('-u') else: args.append('-d "{}"'.format(invoice.get_date().strftime('%Y-%m-%d'))) args.append('-aux "{}"'.format(invoice.get_paid_date().strftime('%Y-%m-%d'))) # get the code if invoice.id: args.append('-c "{}"'.format(invoice.id)) # get payee (project title) if payee == '': args.append('-p "{}"'.format(project.title)) else: args.append('-p "{}"'.format(payee)) # get the comment if invoice.ledger_comment != '': args.append('-cm "{}"'.format(invoice.ledger_comment)) # now generate the entries / postings / accounts # the client account client_acc = project.client_id + ':' # the tax account tax_acc = ':' + settings.ledgeradd_tax_account # get name and price into list of tuples, if single_account == '' entries = [] if single_account == '': for e in invoice.get_entry_list(): name = e.title.replace('"', '\\"') price = e.get_price( entry_list=invoice.get_entry_list(), wage=invoice.get_wage(project=project), round_price=invoice.get_round_price() ) entries.append( ( '{}{}'.format( client_acc, name ), str(price * -1) ) ) # also add a posting for the tax, if it exists tax = e.get_price_tax( entry_list=invoice.get_entry_list(), wage=invoice.get_wage(project=project), round_prive=invoice.get_round_price() ) if tax != 0: # append a posting for tax as well entries.append( ( '{}{}{}'.format( client_acc, name, tax_acc.replace('{TAX_PERCENT}', str(e.get_tax_percent())) ), str(tax * -1) ) ) # otherwise get only name from single_account and price of whole invoice else: entries.append( ( '{}{}'.format( client_acc, single_account ), str(invoice.get_price_total( wage=invoice.get_wage(project=project), project=project, round_price=invoice.get_round_price() ) * -1) ) ) # also add tax, if it exists tax_total = invoice.get_price_tax_total( wage=invoice.get_wage(project=project), project=project, round_price=invoice.get_round_price() ) if tax_total != 0: entries.append( ( '{}{}{}'.format( client_acc, single_account, tax_acc.replace('{TAX_PERCENT}', '').strip() ), str(tax_total * -1) ) ) # append the parameter for the account for e in entries: args.append('-acc "{}" "{}"'.format(e[0], e[1])) # append the receiving account args.append('-acc "{}"'.format(settings.ledgeradd_receiving_account)) # ledgeradd has to run in non-GUI, quiet and forced args.append('-n') args.append('-q') args.append('-f') # return the mighty parameter as one string return ' '.join(args)
def replacer( text=None, settings=None, global_list=None, client=None, project=None, offerinvoice=None ): """ Replace {...} inside text with stuff from client or project ... or the date. If no text is given, the function will return the replacement dict instead. Otherwise the given text will be returned with replaced values. """ is_settings = check_objects.is_settings(settings) is_global_list = check_objects.is_list(global_list) is_client = check_objects.is_client(client) is_project = check_objects.is_project(project) is_offerinvoice = ( check_objects.is_offer(offerinvoice) or check_objects.is_invoice(offerinvoice) ) # replace stuff replace_me = ReplacementDict() # if text is None: # return replace_me # else: # return text.format_map(replace_me) # simple date stuff replace_me['YEAR'] = date.today().strftime('%Y') replace_me['MONTH'] = date.today().strftime('%m') replace_me['DAY'] = date.today().strftime('%d') # global lists related if is_global_list and is_settings: # get active + inactive lists all_list = global_list.get_active_and_inactive_list( settings=settings ) all_clients = all_list.client_list all_projects = all_list.project_list # get all offers all_offers = set([off for o in all_projects for off in o.get_offer_list()]) # get all invoices all_invoices = set([inv for i in all_projects for inv in i.get_invoice_list()]) # general count stuff replace_me['CLIENT_COUNT'] = str(len(all_clients) + 1) replace_me['PROJECT_COUNT'] = str(len(all_projects) + 1) replace_me['OFFER_COUNT'] = str( settings.get_offer_count_offset() + len(all_offers) + 1 ) replace_me['INVOICE_COUNT'] = str( settings.get_invoice_count_offset() + len(all_invoices) + 1 ) # try to get highest invoice id from existing ID strings got_ids = [0] for inv_id in [inv.id for inv in all_invoices]: try: got_ids.append(int(inv_id)) except Exception: pass replace_me['INVOICE_COUNT_B'] = str( max(got_ids) + 1 ) # project related if is_project: replace_me['PROJECT_TITLE'] = project.title replace_me['PROJECT_OFFER_COUNT'] = str(len(project.get_offer_list()) + 1) replace_me['PROJECT_INVOICE_COUNT'] = str(len(project.get_invoice_list()) + 1) # client related if is_client: replace_me['CLIENT_ID'] = ( code_str(client.client_id) ) replace_me['CLIENT_COMPANY'] = ( code_str(client.company) ) replace_me['CLIENT_COMPANY_B'] = ( code_str(client.company_b) ) replace_me['CLIENT_ATTN'] = ( code_str(client.attention) ) replace_me['CLIENT_SALUT'] = ( code_str(client.salutation) ) replace_me['CLIENT_NAME'] = ( code_str(client.name) ) replace_me['CLIENT_FAMILY'] = ( code_str(client.family_name) ) replace_me['CLIENT_FULLNAME'] = ( code_str(client.fullname()) ) replace_me['CLIENT_STREET'] = ( code_str(client.street) ) replace_me['CLIENT_POST_CODE'] = ( code_str(client.post_code) ) replace_me['CLIENT_CITY'] = ( code_str(client.city) ) replace_me['CLIENT_COUNTRY'] = ( code_str(client.country) ) replace_me['CLIENT_TAX_ID'] = ( code_str(client.tax_id) ) replace_me['CLIENT_ADDITIONAL_A'] = ( code_str(client.additional_a) ) replace_me['CLIENT_ADDITIONAL_B'] = ( code_str(client.additional_b) ) replace_me['CLIENT_ADDITIONAL_C'] = ( code_str(client.additional_c) ) # offer / invoice related if is_offerinvoice and is_project: # general offer / invoice stuff replace_me['TITLE'] = offerinvoice.title replace_me['ID'] = offerinvoice.id replace_me['COMMENT'] = offerinvoice.comment replace_me['COMMENT_B'] = offerinvoice.comment_b # dates # date is not None if offerinvoice.get_date() is not None: replace_me['DATE'] = offerinvoice.get_date().strftime( offerinvoice.date_fmt ) else: replace_me['DATE'] = '-' # due date is not None if offerinvoice.get_due_date() is not None: replace_me['DUE_DATE'] = offerinvoice.get_due_date().strftime( offerinvoice.date_fmt ) else: replace_me['DUE_DATE'] = '-' # get due days as well replace_me['DUE_DAYS'] = offerinvoice.get_due_days() # paid date is not None if offerinvoice.get_paid_date() is not None: replace_me['PAID_DATE'] = offerinvoice.get_paid_date().strftime( offerinvoice.date_fmt ) else: replace_me['PAID_DATE'] = '-' # finish date and days replace_me['FINISH_DATE'], replace_me['FINISH_DAYS'] = ( offerinvoice.get_finish_date_and_days( project=project ) ) replace_me['FINISH_DATE'] = replace_me['FINISH_DATE'].strftime( offerinvoice.date_fmt ) # delivery date replace_me['DELIVERY'] = offerinvoice.delivery # time related offer / invoice stuff replace_me['TIME_TOTAL'] = offerinvoice.get_time_total() replace_me['COMMODITY'] = offerinvoice.commodity replace_me['WAGE'] = '{} {}/h'.format( offerinvoice.get_wage(project=project), replace_me['COMMODITY'] ) price_total = offerinvoice.get_price_total( wage=offerinvoice.get_wage(project=project), project=project, round_price=offerinvoice.get_round_price() ) replace_me['PRICE_TOTAL'] = '{} {}'.format( price_total, replace_me['COMMODITY'] ) tax_total = offerinvoice.get_price_tax_total( wage=offerinvoice.get_wage(project=project), project=project, round_price=offerinvoice.get_round_price() ) replace_me['TAX_TOTAL'] = '{} {}'.format( tax_total, replace_me['COMMODITY'] ) replace_me['HAS_TAX'] = str(tax_total > 0) replace_me['PRICE_TAX_TOTAL'] = '{} {}'.format( price_total + tax_total, replace_me['COMMODITY'] ) paypal_commodity = 'usd' if replace_me['COMMODITY'] == '$' else 'eur' replace_me['PAYPAL'] = '{}{}'.format( price_total + tax_total, paypal_commodity ) if text is None: return replace_me else: return text.format_map(replace_me)
def export_to_openoffice(self, client=None, project=None, global_list=None, settings=None, template=None, file=None): """Export the offer to an open office file with the secretary module.""" try: import secretary except Exception: return False is_client = check_objects.is_client(client) is_project = check_objects.is_project(project) is_list = check_objects.is_list(global_list) is_settings = check_objects.is_settings(settings) is_template = os.path.isfile(template) one_not_correct = (not is_client or not is_project or not is_list or not is_settings or not is_template) # cancel if one argument is not valid if one_not_correct: return False # cancel if template does not exist if not os.path.isfile(template): return False # get replacement dict replace_me = replacer(settings=settings, global_list=global_list, client=client, project=project, offerinvoice=self) # get extension from template template_name, template_ext = os.path.splitext(template) # get accordingly extension for filename if template_ext == '.ott' or template_ext == '.odt': file_ext = '.odt' elif template_ext == '.ots' or template_ext == '.ods': file_ext = '.ods' else: # cancel if template is no openoffice template return False # get filename with replaced values, if file not empty if file != '': file = file.format_map(replace_me).replace(' ', '_') # use the offer title as filename else: file = self.title.format_map(replace_me).replace(' ', '_') # check if output file exists and alter the name then file_num = 2 file_new = file + file_ext while os.path.isfile(file_new): file_new = file + '_' + str(file_num) + file_ext file_num += 1 file = file_new # replace the replacer for x in replace_me.keys(): replace_me[x] = str(replace_me[x]).format_map(replace_me) # get entries entries = [] position = 0 for e in self._entry_list: position += 1 time = e.get_time(entry_list=self._entry_list) price = e.get_price(entry_list=self._entry_list, wage=self.get_wage(project=project), round_price=self._round_price) price_unit = e.get_unit_price(entry_list=self._entry_list, wage=self.get_wage(project=project), round_price=self._round_price) tax = e.get_price_tax(entry_list=self._entry_list, wage=self.get_wage(project=project), round_price=self._round_price) tax_unit = e.get_unit_price_tax( entry_list=self._entry_list, wage=self.get_wage(project=project), round_price=self._round_price) total = price + tax total_unit = price_unit + tax_unit if type(e) is MultiplyEntry: wage_add = str(e.get_wage_add()) wage_explain = e.wage_add_explain else: wage_add = '0' wage_explain = '' tmp_replacer = ReplacementDict({ 'E_POSITION': position, 'E_TITLE': e.title, 'E_COMMENT': e.comment, 'E_TIME': e.get_time(entry_list=self._entry_list), 'E_QUANTITY': e.get_quantity_str(), 'E_QUANTITY_B': e.get_quantity_b_str(), 'E_PRICE': '{} {}'.format(price, replace_me['COMMODITY']), 'E_UNIT_PRICE': '{} {}'.format(price_unit, replace_me['COMMODITY']), 'E_PRICE_TAX': '{} {}'.format(tax, replace_me['COMMODITY']), 'E_UNIT_PRICE_TAX': '{} {}'.format(tax_unit, replace_me['COMMODITY']), 'E_TOTAL': '{} {}'.format(total, replace_me['COMMODITY']), 'E_UNIT_TOTAL': '{} {}'.format(total_unit, replace_me['COMMODITY']), 'E_TAX_PERCENT': '{}'.format(round(e.get_tax_percent())), 'E_HAS_TAX': (tax > 0), 'E_WAGE_ADD': '{} {}/h'.format(wage_add, replace_me['COMMODITY']), 'E_WAGE_EXPLAIN': wage_explain }) title = e.title.format_map(replace_me) title = title.format(**tmp_replacer) comment = e.comment.format_map(replace_me) comment = comment.format(**tmp_replacer) entries.append({ 'POSITION': position, 'TITLE': title, 'COMMENT': comment, 'TIME': time, 'QUANTITY': e.get_quantity_str(), 'QUANTITY_B': e.get_quantity_b_str(), 'PRICE': '{} {}'.format(price, replace_me['COMMODITY']), 'UNIT_PRICE': '{} {}'.format(price_unit, replace_me['COMMODITY']), 'PRICE_TAX': '{} {}'.format(tax, replace_me['COMMODITY']), 'UNIT_PRICE_TAX': '{} {}'.format(tax_unit, replace_me['COMMODITY']), 'TOTAL': '{} {}'.format(total, replace_me['COMMODITY']), 'UNIT_TOTAL': '{} {}'.format(total_unit, replace_me['COMMODITY']), 'TAX_PERCENT': '{}'.format(round(e.get_tax_percent())), 'HAS_TAX': (tax > 0) }) # final rendering engine = secretary.Renderer() # try to replace stuff in the template try: result = engine.render(template, entries=entries, data=replace_me) output = open(file, 'wb') output.write(result) output.close() return True except Exception: print('TEMPLATE PROBABLY HAS INVALID VARIABLES') return False
def get_invoice_entries_from_time_journal( settings=None, global_list=None, presets=None, client=None, project=None, invoice=None ): """Return list with invoice entries from ledger time journal.""" is_settings = check_objects.is_settings(settings) is_list = check_objects.is_list(global_list) is_presets = check_objects.is_preset(presets) is_client = check_objects.is_client(client) is_project = check_objects.is_project(project) is_invoice = check_objects.is_invoice(invoice) if ( not is_settings or not is_list or not is_presets or not is_client or not is_project or not is_invoice ): return False # get a dict holding the time account names and its Decimal hours time = get_time_entries( ledger_output_lines=get_ledger_output( settings=settings, project_title=project.title ) ) if time is False: return False # generate a list of entries entries = [] for t in time: # get the base for the time account # search the time entry account name in the presets, # which also have 'AUTO' in their name base = False name_splitted = t.lower().split(' ') + ['auto'] for x in presets.invoice_entry_list: name_in_presets = [ i for i in name_splitted if i in x['name'].lower() ] if len(name_in_presets) > 1: base = x['item'] base.title = t base.set_hour_rate(1) break # not found: generate a new multiplyentry if base is False: base = NewMultiplyEntry( settings=settings, global_list=global_list, client=client, project=project ) # set values base.title = t base.set_hour_rate(1) # set the entry's quantity to the hours form the time entry base.set_quantity(time[t]) # append it to the entries entries.append(base.copy()) return entries