Rechnungen erstellen und verfolgen

11

Alle 2 Wochen generiert das System die Rechnungen für die Unternehmen.

Das Unternehmen erhält jeden Monat am 1. und 16. eine Rechnung. (Es wird alle 2 Wochen über Cron Job ausgeführt. Es durchsucht die Auftragstabelle und fügt sie dann in die Rechnungstabelle ein. Gibt es eine Alternative?)

Die ordersTabelle enthält eine Liste der Kundenbestellungen und gibt an, zu welchem ​​Unternehmen sie gehört ( orders.company_id)

Die invoiceTabelle berechnet die Gesamtkosten der Bestellungen aus der ordersTabelle.

Ich versuche herauszufinden, wie man eine vernünftige Rechnungsverfolgung gestaltet. Irgendwann muss mir das Unternehmen die Gebühren schicken oder irgendwann sende ich ihnen die Gebühren ( invoice.amount)

Ich muss die Rechnungen wie folgt verfolgen:

  • wenn die Firma mir den Betrag geschickt hat
  • Wann habe ich den Betrag an die Firma geschickt?
  • Wie viel Betrag wurde von der Firma erhalten
  • Wie viel Betrag habe ich an die Firma geschickt?
  • Habe ich den vollen Betrag erhalten (wenn nicht, was muss ich auf der Datenbank aktualisieren?)
  • Rechnungsstatus (Rechnung gesendet, storniert, erhaltener Betrag, gesendeter Betrag)

Hier ist das Datenbankdesign, das ich mir ausgedacht habe:

Firmentisch

mysql> select * from company;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | Company A |
|  2 | Company B |
+----+-----------+

Kunden können ein Unternehmen von meiner Website auswählen.

Bestelltabelle

mysql> select * from orders;
+----+---------+------------+------------+---------------------+-----------+
| id | user_id | company_id | total_cost | order_date          | status_id |
+----+---------+------------+------------+---------------------+-----------+
|  1 |       5 |          2 |      25.00 | 2012-02-03 23:30:24 |         1 |
|  2 |       7 |          2 |      30.00 | 2012-02-13 18:06:12 |         1 |
+----+---------+------------+------------+---------------------+-----------+

Zwei Kunden haben die Produkte bei Firma B ( orders.company_id = 2) bestellt. Ich weiß, dass die Auftragsfelder nicht ausreichen, nur für Sie vereinfacht.

Bestellungstabellen

mysql> select * from orders_products;
+----+----------+------------+--------------+-------+
| id | order_id | product_id | product_name | cost  |
+----+----------+------------+--------------+-------+
|  1 |        1 |         34 | Chair        | 10.00 |
|  2 |        1 |         25 | TV           | 10.00 |
|  3 |        1 |         27 | Desk         |  2.50 |
|  4 |        1 |         36 | Laptop       |  2.50 |
|  5 |        2 |         75 | PHP Book     | 25.00 |
|  6 |        2 |         74 | MySQL Book   |  5.00 |
+----+----------+------------+--------------+-------+

Liste der Produkte, die Kunden bestellt haben.

Rechnungstabelle

mysql> select * from invoice;
+----+------------+------------+---------------------+--------+-----------+
| id | company_id | invoice_no | invoice_date        | amount | status_id |
+----+------------+------------+---------------------+--------+-----------+
|  7 |          2 |        123 | 2012-02-16 23:59:59 |  55.00 |         1 |
+----+------------+------------+---------------------+--------+-----------+

Hier stecke ich ziemlich fest in der Gestaltung von Rechnungstabellen. Ich bin mir nicht sicher, wie es gemacht werden soll. Rechnungen werden alle 2 Wochen erstellt. Aus dem Ergebnis ergibt sich ein Beispiel von invoice.amount55,00, da es aus der orders.company_id = 2Tabelle berechnet wurde

Wenn der invoice.amountWert -50,00 (minus) beträgt, muss das Unternehmen mir den Gebührenbetrag senden.

Wenn der invoice.amountWert 50,00 ist, muss ich dem Unternehmen die Gebühren zusenden.

Die status_id könnte sein: (1) Gesendete Rechnung, (2) Storniert, (3) Abgeschlossen

Muss ich invoice_idder ordersTabelle ein Feld hinzufügen ? Aktualisieren Sie das orders.invoice_idFeld, wenn die Zeile in die Rechnungstabelle eingefügt wurde.

Rechnung_Zahlungstabelle

mysql> select * from invoice_payment;
+----+------------+-----------------+-------------+---------------------+---------------------+
| id | invoice_id | amount_received | amount_sent | date_received       | date_sent           |
+----+------------+-----------------+-------------+---------------------+---------------------+
|  1 |          1 |            0.00 |       55.00 | 0000-00-00 00:00:00 | 2012-02-18 22:20:53 |
+----+------------+-----------------+-------------+---------------------+---------------------+

Hier kann ich die Transaktion verfolgen und aktualisieren. Die Zahlung erfolgt über BACS.

Ist das ein gutes Tischdesign oder was muss ich verbessern? Welche Felder und Tabellen sollte ich hinzufügen?

Wenn die Rechnung erstellt wurde und ich später die Änderungen in orders_productsoder ordersTabellen vornehmen muss - sollte das invoice.amountFeld neu berechnet werden? (Ich werde PHP / MySQL verwenden).

SQL Dump :

CREATE TABLE IF NOT EXISTS `company` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(25) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

INSERT INTO `company` (`id`, `name`) VALUES
(1, 'Company A'),
(2, 'Company B');

CREATE TABLE IF NOT EXISTS `invoice` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `company_id` int(11) NOT NULL,
  `invoice_no` int(11) NOT NULL,
  `invoice_date` datetime NOT NULL,
  `amount` decimal(6,2) NOT NULL,
  `status_id` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;


INSERT INTO `invoice` (`id`, `company_id`, `invoice_no`, `invoice_date`, `amount`, `status_id`) VALUES
(7, 2, 123, '2012-02-16 23:59:59', '55.00', 1);


CREATE TABLE IF NOT EXISTS `invoice_payment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `invoice_id` int(11) NOT NULL,
  `amount_received` decimal(6,2) NOT NULL,
  `amount_sent` decimal(6,2) NOT NULL,
  `date_received` datetime NOT NULL,
  `date_sent` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

INSERT INTO `invoice_payment` (`id`, `invoice_id`, `amount_received`, `amount_sent`, `date_received`, `date_sent`) VALUES
(1, 1, '0.00', '55.00', '0000-00-00 00:00:00', '2012-02-18 22:20:53');


CREATE TABLE IF NOT EXISTS `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `company_id` int(11) NOT NULL,
  `total_cost` decimal(6,2) NOT NULL,
  `order_date` datetime NOT NULL,
  `status_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;


INSERT INTO `orders` (`id`, `user_id`, `company_id`, `total_cost`, `order_date`, `status_id`) VALUES
(1, 5, 2, '25.00', '2012-02-03 23:30:24', 1),
(2, 7, 2, '30.00', '2012-02-13 18:06:12', 1);


CREATE TABLE IF NOT EXISTS `orders_products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(11) NOT NULL,
  `product_id` int(11) NOT NULL,
  `product_name` varchar(100) NOT NULL,
  `cost` decimal(6,2) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;

INSERT INTO `orders_products` (`id`, `order_id`, `product_id`, `product_name`, `cost`) VALUES
(1, 1, 34, 'Chair', '10.00'),
(2, 1, 25, 'TV', '10.00'),
(3, 1, 27, 'Desk', '2.50'),
(4, 1, 36, 'Laptop', '2.50'),
(5, 2, 75, 'PHP Book', '25.00'),
(6, 2, 74, 'MySQL Book', '5.00');

Fühlen Sie sich frei, wenn Sie hier aktualisieren / Tabellen zur Antwort hinzufügen möchten.

Vielen Dank

Ich komme wieder
quelle

Antworten:

8

Cash Matching

Dies ist ein Cash-Matching-Problem. Sie können dies auf einer von zwei Ebenen verfolgen:

  • Vergleichen Sie die in Rechnung gestellten Zahlen mit Bargeld (etwas schlampig, aber so machen es die meisten Lloyd's Syndicates, die oft als "schriftlicher oder unterschriebener" Bericht bezeichnet werden).

  • Behalten Sie explizite Barzuweisungen aus Barzahlungen bei, die nach Rechnungen aufgeschlüsselt sind.

Aufgrund Ihrer Frage denke ich, dass Sie Letzteres tun möchten.

In der Regel erfolgt dies durch einen separaten Satz von Bargeldtransaktionen und eine Überbrückungstabelle, in der Barzahlungen den Rechnungen zugeordnet sind. Wenn die Werte gleich sind oder die Barzahlung mit einer einzigen Rechnungsreferenz geliefert wird, können Sie die Zuordnung automatisch vornehmen. Wenn es eine M: M-Beziehung zwischen Rechnungen und Zahlungen gibt, müssen Sie einen manuellen Abgleich durchführen (dies ist automatisch eine Variante des Rucksackproblems ).

Ein grundlegendes Cash Matching System

Stellen Sie sich vor, Sie haben eine Rechnungstabelle, eine Barzahlungstabelle und eine Zuordnungstabelle. Wenn Sie eine Rechnung ausstellen, richten Sie in der Rechnungstabelle einen Rechnungsdatensatz und in der Zuordnungstabelle einen Datensatz für Forderungen oder Verbindlichkeiten ein.

  • Rechnung Nr. 1, 100 US-Dollar

  • Zuordnung: Ein Datensatz mit einem Verweis auf Rechnung Nr. 1, Transaktionstyp "Forderung" und geschuldeten 100 USD. Kein Hinweis auf eine Barzahlung in diesem Datensatz.

Jetzt erhalten Sie eine Barzahlung von 100 US-Dollar

  • Barzahlungen (chq # 12345): 100 USD

  • Zuordnung: Ein Datensatz mit einem Verweis auf Rechnung Nr. 1 und CHQ Nr. 12345, Transaktionstyp 'Bargeld' und -100 geschuldet (100 USD bezahlt).

Sie können dies auf eine M: M-Beziehung verallgemeinern, in der Sie mehrere Zahlungen für eine einzelne Rechnung oder eine Zahlung für mehrere Rechnungen erhalten. Diese Struktur macht es auch recht einfach, Kreditkontrollberichte zu erstellen. Der Bericht muss nur Rechnungen finden, die älter als (sagen wir) 180 Tage sind und noch ausstehende Salden aufweisen.

Hier ist ein Beispiel für das Schema sowie einige Szenarien und eine Abfrage zu veralteten Schulden. Leider habe ich keine laufende MySQL-Instanz zur Hand, daher ist diese für SQL Server.

-- ==============================================================
-- === CashMatch.sql ============================================
-- ==============================================================
--


-- === Invoices =================================================
--
create table Invoice (
       InvoiceID        int identity (1,1) not null
      ,InvoiceRef       varchar (20)
      ,Amount           money
      ,InvoiceDate      datetime
)
go

alter table Invoice
  add constraint PK_Invoice 
      primary key nonclustered (InvoiceID)
go


-- === Cash Payments ============================================
--
create table CashPayment (
       CashPaymentID    int identity (1,1) not null
      ,CashPaymentRef   varchar (20)
      ,Amount           money
      ,PaidDate         datetime
)
go

alter table CashPayment
  add constraint PK_CashPayment
      primary key nonclustered (CashPaymentID)
go




-- === Allocations ==============================================
--
create table Allocation (
       AllocationID       int identity (1,1) not null
      ,CashPaymentID      int  -- Note that some records are not
      ,InvoiceID          int  -- on one side.
      ,AllocatedAmount    money
      ,AllocationType     varchar (20)
      ,TransactionDate    datetime
)
go

alter table Allocation
  add constraint PK_Allocation
      primary key nonclustered (AllocationID)
go


-- ==============================================================
-- === Scenarios ================================================
-- ==============================================================
--
declare @Invoice1ID int
       ,@Invoice2ID int
       ,@PaymentID int


-- === Raise a new invoice ======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('001', 100, '2012-01-01')

set @Invoice1ID = @@identity

insert Allocation (
       InvoiceID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice1ID, 100, '2012-01-01', 'receivable')


-- === Receive a payment ========================================
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('12345', 100, getdate())

set @PaymentID = @@identity

insert Allocation (
       InvoiceID
      ,CashPaymentID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice1ID, @PaymentID, -100, getdate(), 'paid')



-- === Raise two invoices =======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('002', 75, '2012-01-01')

set @Invoice1ID = @@identity

insert Allocation (
       InvoiceID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice1ID, 75, '2012-01-01', 'receivable')


insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('003', 75, '2012-01-01')

set @Invoice2ID = @@identity

insert Allocation (
       InvoiceID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice2ID, 75, '2012-01-01', 'receivable')


-- === Receive a payment ========================================
-- The payment covers one invoice in full and part of the other.
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('23456', 120, getdate()) 

set @PaymentID = @@identity

insert Allocation (
       InvoiceID
      ,CashPaymentID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice1ID, @PaymentID, -75, getdate(), 'paid')

insert Allocation (
       InvoiceID
      ,CashPaymentID
      ,AllocatedAmount
      ,TransactionDate
      ,AllocationType
) values (@Invoice2ID, @PaymentID, -45, getdate(), 'paid')



-- === Aged debt report ========================================
--
select i.InvoiceRef
      ,sum (a.AllocatedAmount)                 as Owing
      ,datediff (dd, i.InvoiceDate, getdate()) as Age
  from Invoice i
  join Allocation a
    on a.InvoiceID = i.InvoiceID
 group by i.InvoiceRef
         ,datediff (dd, i.InvoiceDate, getdate())
having sum (a.AllocatedAmount) > 0
ConcernedOfTunbridgeWells
quelle
Meins hat separate Tabellen für Rechnungen und Zahlungen. Sie können eine gemeinsame Tabelle mit interner Verknüpfung verwenden. Cash Matching wird in Buchhaltungssystemen häufig auf diese Weise implementiert.
ConcernedOfTunbridgeWells
Ich habe es geschafft, Ihr SQL Server-Beispiel in MySQL zu übersetzen. Ich habe es durchgemacht und jetzt sehr gut verstanden. Was wäre AllocationType, wenn ich Kundengeld senden möchte? Muss ich mich auch in die CashPaymentTabelle einfügen (sagen wir, ich bezahle sie über BACS)?
Ich werde
1
Ja, Sie möchten einen Barzahlungsdatensatz für eingehende und ausgehende Zahlungen. Die tatsächlichen Transaktionstypen für die Cash Matching-Transaktionen liegen bei Ihnen.
ConcernedOfTunbridgeWells
1
Wenn Sie möchten, können Sie Transaktionen aus Rechnungen in beide Richtungen mit einer einzigen Abrechnungszahlung abgleichen. Beispiel: Ausgehende Rechnung für 100 USD, eingehende Rechnung für 50 USD (-50 USD) und Ausgleichszahlung für 50 USD werden mit beiden Rechnungen abgeglichen.
ConcernedOfTunbridgeWells