Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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