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) + "'.")
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()
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
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))
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) + "'.")
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))
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.")
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))
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
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.")
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) + "'.")
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
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) + '.')
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.")
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) + "." )
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()
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) + ".")
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)
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) + "'.")
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)
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]
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
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();
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()))
def getClientByHandle(handle, session): client = session.query(Client).filter_by(handle=handle).first() dieIf(not client, "Client not found by handle '" + handle + "'.") return client
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))
def getTotal(self): """ Gets the total price. """ dieIf(True, "getTotal not implemented.")
def getNumberOfUnits(self): """ Gets the total number of units, even if the units do not have the same price. """ dieIf(True, "getNumberOfUnits not implemented.")
def getPricePerUnit(self): """ Gets the price of unit, if the price of all units is equal. Otherwise, returns None. """ dieIf(True, "getNumberOfUnits not implemented.")