コード例 #1
0
ファイル: subrow.py プロジェクト: Jasu/Lokki
def commandSubrowAdd(args, session):
    invoice = beginRowCommand(args, session)
    subrow = Subrow()
    subrow.row = findRow(args, invoice, session, type=CompositeRow)
    # Note: Indexing starts from one here. Setting subrow.row in the previous
    # statement causes its insertion in subrow.row.subrows.
    subrow.index = len(subrow.row.subrows) 
    subrow.title = args.title
    subrow.num_units = args.num_units
    subrow.price_per_unit = args.price_per_unit
    if args.note:
        subrow.note = args.note
    if args.external_source:
        subrow.external_source = args.external_source
    if args.external_id:
        subrow.external_id = args.external_id
    if args.external_source and args.external_id:
        query = (session.query(Subrow)
                 .filter_by(external_source=args.external_source)
                 .filter_by(external_id=args.external_id))
        dieIf(query.first(), 'External id already exists.')

    session.add(subrow)
    session.commit()

    print("Added subrow '" + str(subrow.index) + "'.")
コード例 #2
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceRemove(args, session):
    invoice = session.query(Invoice).filter_by(invoice_number=args.invoice_number).first()
    dieIf(not invoice, "Invoice not found by number '" + args.invoice_number + "'.")
    dieIf(invoice.is_billed, "Cannot remove a billed invoice.")

    session.delete(invoice)
    session.commit()
コード例 #3
0
ファイル: row.py プロジェクト: Jasu/Lokki
def beginRowCommand(args, session, readonly=False):
  dieIf(not isConfigurationValid(session), 
    "Cannot execute row commands with incomplete configuration.")
  invoice = findInvoice(session, args)
  dieIf(not readonly and invoice.is_billed, 
    "Cannot execute row commands on a billed invoice.")
  return invoice
コード例 #4
0
ファイル: subrow.py プロジェクト: Jasu/Lokki
def commandSubrowGet(args, session):
    invoice = beginRowCommand(args, session)
    row = findRow(args, invoice, session, CompositeRow)
    subrow = findSubrow(row, args.subrow_index)
    dieIf(not hasattr(subrow, args.setting_name), 
          "Setting '" + args.setting_name + "' not found.")
    print(getattr(subrow, args.setting_name))
コード例 #5
0
ファイル: composite.py プロジェクト: Jasu/Lokki
def commandCompositeAdd(args, session):
  invoice = beginRowCommand(args, session)
  row = CompositeRow()
  row.index = getNextRowIndex(invoice)
  row.invoice = invoice
  row.title = args.title
  if args.vat:
    row.vat = args.vat
  else:
    row.vat = getSetting(session, 'default-vat')
    dieIf(not row.vat, "default-vat is not set and no VAT was provided.")
  if args.note:
    row.note = args.note
  if args.external_source:
    row.external_source = args.external_source
  if args.external_id:
    row.external_id = args.external_id

  if args.external_source and args.external_id:
    query = (session.query(Row)
             .filter_by(external_source=args.external_source)
             .filter_by(external_id=args.external_id))
    dieIf(query.first(), 'External id already exists.')
  
  session.add(row)
  session.commit()

  if args.json:
    jsonPrintRow(row)
  else:
    print("Added row '" + str(row.index) + "'.")
コード例 #6
0
ファイル: client.py プロジェクト: Jasu/Lokki
def commandClientGet(args, session):
  client = getClientByHandle(args.handle, session)

  dieIf(not hasattr(client, args.setting_name), 
    "Setting name '" + args.setting_name + "' does not exist.")

  print(getattr(client, args.setting_name))
コード例 #7
0
ファイル: client.py プロジェクト: Jasu/Lokki
def commandClientAdd(args, session):
  client = Client()
  client.handle = args.handle

  if 'name' in args and args.name:
    client.name = args.name
  if 'address' in args and args.address:
    client.address = args.address
  if 'address_2' in args and args.address_2:
    client.address_2 = args.address_2
  if 'zip_code' in args and args.zip_code:
    client.zip_code = args.zip_code
  if 'city' in args and args.city:
    client.city = args.city
  if 'country' in args and args.country:
    client.country = args.country
  if 'company_number' in args and args.company_number:
    client.company_number = args.company_number
  if 'vat_number' in args and args.vat_number:
    client.vat_number = args.vat_number
  if 'client_number' in args and args.client_number:
    clientByNumber = (session.query(Client)
                        .filter_by(client_number=args.client_number)
                        .first())
    dieIf(clientByNumber, 
      "Client already exists by client number '" + args.client_number + "'.")
    client.client_number = args.client_number

  session.add(client)
  session.commit()

  print("Client was added.")
コード例 #8
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceGet(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")

    invoice = findInvoice(session, args)

    dieIf(not hasattr(invoice, args.setting_name), "Invoice setting '" + args.setting_name + "' does not exist.")

    print(getattr(invoice, args.setting_name))
コード例 #9
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def getNextInvoiceNumberAndIncrement(session):
  """
  Does not commit the session, to allow transaction semantics.
  """
  setting = session.query(Setting).filter_by(name='next-invoice-number').first()
  dieIf(not setting, "Next invoice number is not set.")
  result = setting.value
  setting.value = int(setting.value) + 1
  return result
コード例 #10
0
ファイル: client.py プロジェクト: Jasu/Lokki
def commandClientSet(args, session):
  client = getClientByHandle(args.handle, session)

  dieIf(not hasattr(client, args.setting_name), 
    "Setting name '" + args.setting_name + "' does not exist.")

  setattr(client, args.setting_name, args.setting_value)
  session.commit()

  print("Client '" + args.handle + "' was updated.")
コード例 #11
0
ファイル: row.py プロジェクト: Jasu/Lokki
def commandRowSet(args, session):
  invoice = beginRowCommand(args, session)
  row = findRow(args, invoice, session, type=None)
  dieIf(not hasattr(row, args.setting_name), 
    "Setting '" + args.setting_name + "' does not exist.")

  setattr(row, args.setting_name, args.setting_value)

  session.commit()

  print("Updated row '" + str(row.index) + "'.")
コード例 #12
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def findInvoice(session, args):
  if args.invoice_number:
    invoice = session.query(Invoice).filter_by(invoice_number=args.invoice_number).first()
    dieIf(not invoice, 
      "Invoice not found by invoice number '" + args.invoice_number+"'.")
    return invoice
  else:
    invoice = (session.query(Invoice)
                      .order_by(Invoice.time_added.desc())
                      .first())
    dieIf(not invoice, "No invoices exist yet.")
    return invoice
コード例 #13
0
ファイル: subrow.py プロジェクト: Jasu/Lokki
def commandSubrowSet(args, session):
    invoice = beginRowCommand(args, session)
    row = findRow(args, invoice, session, CompositeRow)
    subrow = findSubrow(row, args.subrow_index)
    dieIf(not hasattr(subrow, args.setting_name), 
          "Setting '" + args.setting_name + "' not found.")
    setattr(subrow, args.setting_name, args.setting_value)
    session.commit()
  
    print('Updated subrow ' + str(subrow.index)
          + ' of row ' + str(row.index)
          + ' of invoice ' + str(invoice.invoice_number) + '.')
コード例 #14
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceUnbill(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")

    invoice = findInvoice(session, args)

    dieIf(not invoice.is_billed, "Invoice " + str(invoice.invoice_number) + " was not billed.")

    invoice.is_billed = False
    session.commit()

    triggerEvent(session, "unbill", _getTemplateArguments(invoice))

    print("Marked invoice '" + str(invoice.invoice_number) + "' as not billed.")
コード例 #15
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceMerge(args, session):
    sourceInvoice = session.query(Invoice).filter_by(invoice_number=args.source_invoice_number).first()

    targetInvoice = session.query(Invoice).filter_by(invoice_number=args.target_invoice_number).first()

    dieIf(not sourceInvoice, "Source invoice not found.")
    dieIf(not targetInvoice, "Target invoice not found.")
    dieIf(sourceInvoice.is_billed, "Source invoice is billed.")
    dieIf(targetInvoice.is_billed, "Target invoice is billed.")

    # Make copy of the array, since elements are removed from it during moving,
    # making for loop to iterate only 1/2 rows.
    rows = sourceInvoice.rows[:]
    for row in rows:
        print("Moving row #" + str(row.id) + ".")
        row.invoice = targetInvoice

    session.commit()

    compressIndices(session, Row, invoice_id=targetInvoice.id)

    session.commit()

    print(
        "Moved rows from invoice #"
        + str(args.source_invoice_number)
        + " to invoice #"
        + str(args.target_invoice_number)
        + "."
    )
コード例 #16
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceSet(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")

    invoice = findInvoice(session, args)

    dieIf(not hasattr(invoice, args.setting_name), "Invoice setting '" + args.setting_name + "' does not exist.")

    if args.setting_name in ["date", "due_date"]:
        value = parseDate(args.setting_value)
    else:
        value = args.setting_value

    setattr(invoice, args.setting_name, value)
    session.commit()
コード例 #17
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceAdd(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")

    invoice = Invoice()

    if args.client_handle:
        client = getClientByHandle(args.client_handle, session)
    else:
        clientId = getSetting(session, "default-client")
        dieIf(not clientId, "Default client is not set and no --client-handle was set.")
        client = session.query(Client).filter_by(id=clientId).first()

    dieIf(not client, "Default client not set and no --client-handle was set.")

    initializeClientFields(client, invoice)

    initializeSellerFields(session, invoice)

    # Date
    if args.date:
        date = parseDate(args.date)
    else:
        date = datetime.now()

    invoice.date = date

    if args.duedate:
        duedate = parseDate(args.duedate)
    elif args.duedays:
        duedate = date + timedelta(days=int(args.duedays))
    else:
        duedays = getSetting(session, "default-due-days")
        dieIf(not duedays, "Due date not specified and no default-due-days.")
        duedate = date + timedelta(days=int(duedays))

    invoice.due_date = duedate

    # Invoice number
    if args.invoice_number:
        invoice.invoice_number = args.invoice_number
    else:
        invoice.invoice_number = getNextInvoiceNumberAndIncrement(session)

    # Reference
    invoice.reference = getReference(invoice.invoice_number, invoice.client_number)

    invoice.time_added = datetime.now()

    session.add(invoice)
    session.commit()

    if args.json:
        _jsonPrintInvoice(invoice)
    else:
        print("Invoice created with number " + str(invoice.invoice_number) + ".")
コード例 #18
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceList(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")
    invoices = session.query(Invoice)
    table = PrettyTable(["Number", "Date", "Reference", "Client", "Sum", "VAT", "With VAT", "Status"])
    table.padding_width = 1

    for invoice in invoices:
        table.add_row(
            [
                invoice.invoice_number,
                invoice.date,
                invoice.reference,
                invoice.client.name,
                invoice.getTotal(),
                invoice.getTotalVAT(),
                invoice.getTotalWithVAT(),
                "Billed" if invoice.is_billed else "Not billed",
            ]
        )

    print(table)
コード例 #19
0
ファイル: row.py プロジェクト: Jasu/Lokki
def commandRowMv(args, session):
    dieIf(not isConfigurationValid(session), 
        "Cannot execute row commands with incomplete configuration.")
    src_invoice = (session.query(Invoice)
        .filter_by(invoice_number=args.src_invoice_number)
        .first())
    dst_invoice = (session.query(Invoice)
        .filter_by(invoice_number=args.dst_invoice_number)
        .first())
    dieIf(src_invoice.is_billed, 
        "Cannot execute row commands on a billed source invoice.")
    dieIf(dst_invoice.is_billed, 
        "Cannot execute row commands on a billed source invoice.")
    dieIf(int(args.row) > len(src_invoice.rows), 'Row index is too large.')
    dieIf(int(args.row) < 1, 'Row index below one.')
    row = src_invoice.rows[int(args.row) - 1]
    row.invoice = dst_invoice

    session.commit()
    compressIndices(session, Row, invoice=dst_invoice)
    compressIndices(session, Row, invoice=src_invoice)
    session.commit()
    print("Moved row '" + str(row.index) + "'.")
コード例 #20
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceGenerate(args, session):
    if args.template:
        template = args.template
    else:
        template = getSetting(session, "default-invoice-template")
        lokki_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        template = lokki_dir + "/" + template

    dieIf(not template, "No --template provided and default-invoice-template not set.")

    invoice = findInvoice(session, args)

    templateArguments = _getTemplateArguments(invoice)
    templateArguments["show_details"] = not args.hide_details and len(templateArguments["composite_rows"])

    with open(template, "r") as template:
        templateString = template.read()

    renderedInvoice = pystache.render(templateString, templateArguments)

    if args.filename:
        targetPath = args.filename
    else:
        targetFilenameTemplate = getSetting(session, "invoice-filename-template")
        targetPath = pystache.render(targetFilenameTemplate, templateArguments)

    targetPath = os.path.normpath(targetPath)

    templateArguments["output_path"] = targetPath
    templateArguments["output_basename"] = os.path.basename(targetPath)
    (templateArguments["output_path_no_ext"], templateArguments["output_path_ext"]) = os.path.splitext(targetPath)

    with open(targetPath, "w") as output:
        output.write(renderedInvoice)

    triggerEvent(session, "generate", templateArguments)
コード例 #21
0
ファイル: compositerow.py プロジェクト: Jasu/Lokki
def findSubrow(row, subrowIndex):
    if subrowIndex:
        dieIf(int(subrowIndex) < 1, 
              "Subrow index is too small.")
        dieIf(int(subrowIndex) > len(row.subrows), 
              "Subrow index is too large.")
        return row.subrows[int(subrowIndex) - 1]
    else:
        dieIf(len(row.subrows) == 0, "The selected row contains no subrows.")
        return row.subrows[len(row.subrows) - 1]
コード例 #22
0
ファイル: row.py プロジェクト: Jasu/Lokki
def findRow(args, invoice, session, type=SimpleRow):
  index = None
  if 'index' in args and args.index:
    index = args.index
  elif 'row' in args and args.row:
    index = args.row
  if index:
    dieIf(int(index) > len(invoice.rows), 'Row index is too large.')
    dieIf(int(index) < 1, 'Row index below one.')
    row = invoice.rows[int(index) - 1]
  else:
    row = invoice.rows[-1]
  dieIf(type and not isinstance(row, type), 'Selected row is not a simple row.')
  return row
コード例 #23
0
ファイル: composite.py プロジェクト: Jasu/Lokki
def commandCompositeMerge(args, session):
    invoice = beginRowCommand(args, session)
    sourceRow = invoice.rows[int(args.source_index) - 1] 
    targetRow = invoice.rows[int(args.target_index) - 1] 
    dieIf(not isinstance(sourceRow, CompositeRow), 
          "Source row is not a composite row.");
    dieIf(not isinstance(targetRow, CompositeRow), 
          "Target row is not a composite row.");
    dieIf(sourceRow == targetRow, 
          "Source and target rows cannot be the same row.")

    subrows = sourceRow.subrows[:]
    for subrow in subrows:
        subrow.row = targetRow

    session.delete(sourceRow);

    session.commit();

    compressIndices(session, Subrow, row_id=targetRow.id)
    compressIndices(session, Row, invoice_id=invoice.id)

    session.commit();
コード例 #24
0
ファイル: invoice.py プロジェクト: Jasu/Lokki
def commandInvoiceShow(args, session):
    dieIf(not isConfigurationValid(session), "Cannot execute invoicing commands with incomplete configuration.")

    invoice = findInvoice(session, args)

    if args.json:
        _jsonPrintInvoice(invoice)
    else:
        table = PrettyTable(["Seller info", ""])
        table.align["Seller info"] = "r"
        table.align[""] = "l"

        table.add_row(["Name", invoice.seller_name])
        table.add_row(["Address", invoice.seller_address])
        table.add_row(["ZIP code", invoice.seller_zip_code])
        table.add_row(["City", invoice.seller_city])
        if invoice.seller_country:
            table.add_row(["Country", invoice.seller_country])
        if invoice.seller_company_number:
            table.add_row(["Company number", invoice.seller_company_number])
        if invoice.seller_vat_number:
            table.add_row(["VAT number", invoice.seller_vat_number])

        print(table)
        print("")

        table = PrettyTable(["Client info", ""])
        table.align["Client info"] = "r"
        table.align[""] = "l"

        table.add_row(["Name", invoice.client_name])
        table.add_row(["Client number", invoice.client_number])
        table.add_row(["Address", invoice.client_address])
        table.add_row(["ZIP code", invoice.client_zip_code])
        table.add_row(["City", invoice.client_city])
        if invoice.client_country:
            table.add_row(["Country", invoice.client_country])
        if invoice.client_company_number:
            table.add_row(["Company number", invoice.client_company_number])
        if invoice.client_vat_number:
            table.add_row(["VAT number", invoice.client_vat_number])

        print(table)
        print("")

        table = PrettyTable(["Invoice", ""])
        table.align["Invoice"] = "r"
        table.align[""] = "l"

        table.add_row(["Created", invoice.time_added.strftime("%Y-%m-%d %H:%M:%S")])
        table.add_row(["Date", invoice.date.strftime("%Y-%m-%d")])
        table.add_row(["Due date", invoice.due_date.strftime("%Y-%m-%d")])
        table.add_row(["IBAN", invoice.seller_iban])
        table.add_row(["Invoice number", invoice.invoice_number])
        table.add_row(["Reference", invoice.reference])
        table.add_row(["Is billed", "Yes" if invoice.is_billed else "Not billed"])

        print(table)

        table = PrettyTable(["N", "Item", "Units", "Price/unit", "Total", "*"])
        table.align["N"] = "l"
        table.align["Item"] = "l"
        table.align["Units"] = "l"
        table.align["Price/unit"] = "l"
        table.align["Total"] = "l"
        table.align["*"] = "l"

        for row in invoice.rows:
            extra = []
            if row.note:
                extra.append("note")
            if isinstance(row, CompositeRow):
                extra.append("composite")
            if row.external_source != "lk":
                extra.append(row.external_source)
            table.add_row(
                [
                    row.index,
                    row.title,
                    formatNumber(row.getNumberOfUnits()),
                    formatNumber(row.getPricePerUnit()),
                    formatNumber(row.getTotal()),
                    ", ".join(extra),
                ]
            )

        print("")
        print(table)

        print("")
        print("Total without VAT:   " + formatNumber(invoice.getTotal()))
        print("Total VAT:           " + formatNumber(invoice.getTotalVAT()))
        print("Total with VAT       " + formatNumber(invoice.getTotalWithVAT()))
コード例 #25
0
ファイル: client.py プロジェクト: Jasu/Lokki
def getClientByHandle(handle, session):
  client = session.query(Client).filter_by(handle=handle).first()
  dieIf(not client, "Client not found by handle '" + handle + "'.")
  return client
コード例 #26
0
ファイル: row.py プロジェクト: Jasu/Lokki
def commandRowGet(args, session):
  invoice = beginRowCommand(args, session, readonly=True)
  row = findRow(args, invoice, session, type=None)
  dieIf(not hasattr(row, args.setting_name), 
    "Setting '" + args.setting_name + "' does not exist.")
  print(getattr(row, args.setting_name))
コード例 #27
0
ファイル: row.py プロジェクト: Jasu/Lokki
 def getTotal(self):
   """
   Gets the total price.
   """
   dieIf(True, "getTotal not implemented.")
コード例 #28
0
ファイル: row.py プロジェクト: Jasu/Lokki
 def getNumberOfUnits(self):
   """
   Gets the total number of units, even if the units do not 
   have the same price.
   """
   dieIf(True, "getNumberOfUnits not implemented.")
コード例 #29
0
ファイル: row.py プロジェクト: Jasu/Lokki
 def getPricePerUnit(self):
   """
   Gets the price of unit, if the price of all units is equal.
   Otherwise, returns None.
   """
   dieIf(True, "getNumberOfUnits not implemented.")