Generowanie faktur i śledzenie

11

Co 2 tygodnie system będzie generować faktury dla firm.

Firma otrzyma fakturę 1 i 16 każdego miesiąca. (Będzie uruchamiany za pośrednictwem Cron Job co 2 tygodnie. Skanuje tabelę zamówień, a następnie dodaje do tabeli „faktury”. Czy istnieje alternatywa?)

W orderstabeli znajduje się lista zamówień klientów, a także wskazuje, do której firmy należy ( orders.company_id)

invoiceTabeli obliczyć całkowity koszt zamówień od ordersstołu.

Próbuję wymyślić, jak zaprojektować rozsądne śledzenie faktur. Kiedyś firma będzie musiała przesłać mi opłaty lub innym razem, gdy wyślę im opłaty ( invoice.amount)

Muszę śledzić faktury za pomocą:

  • kiedy firma wyśle ​​mi kwotę
  • kiedy wysłałem kwotę do firmy
  • ile kwoty otrzymano od firmy
  • ile kwoty wysłałem do firmy
  • czy otrzymałem pełną kwotę (jeśli nie, co muszę zaktualizować na Db?)
  • status faktury (faktura wysłana, anulowana, otrzymana kwota, wysłana kwota)

Oto projekt bazy danych, który wymyśliłem:

stolik firmowy

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

Klienci mogą wybrać firmę z mojej strony internetowej.

tabela zamówień

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 |
+----+---------+------------+------------+---------------------+-----------+

dwóch klientów zamówiło produkty od firmy B ( orders.company_id = 2). Wiem, że pola zamówień nie są wystarczające, po prostu uproszczone dla Ciebie.

tabela zamowienia_produkty

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 |
+----+----------+------------+--------------+-------+

Lista produktów zamówionych przez klientów.

tabela faktur

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 |
+----+------------+------------+---------------------+--------+-----------+

Właśnie dlatego utknąłem przy projektowaniu tabel faktur. Nie jestem pewien, jak to zrobić. Faktury będą generowane co 2 tygodnie. Z przykładowego wyniku invoice.amountwynika 55,00, ponieważ został obliczony z orders.company_id = 2tabeli

Jeśli invoice.amountwynosi -50,00 (minus), oznacza to, że firma będzie musiała przesłać mi kwotę opłat.

Jeśli invoice.amountjest 50,00, oznacza to, że muszę wysłać firmie opłaty.

Status_id może być: (1) Wysłana faktura, (2) Anulowana, (3) Ukończona

Czy muszę dodać invoice_idpole w orderstabeli? Zaktualizuj orders.invoice_idpole, gdy wiersz zostanie wstawiony do tabeli „faktury”.

tabela faktury

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 |
+----+------------+-----------------+-------------+---------------------+---------------------+

Tutaj mogę śledzić i aktualizować transakcje. Płatność zostanie dokonana za pośrednictwem BACS.

Czy to dobry projekt stołów lub co muszę poprawić? Jakie pola i tabele powinienem dodać?

Jeśli faktura została wygenerowana, a później muszę dokonać zmian orders_productslub orderstabele - należy ponownie obliczyć invoice.amountpole? (Będę używać PHP / MySQL).

Zrzut SQL :

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');

Zapraszam do aktualizacji / dodawania tabel do odpowiedzi tutaj.

Dzięki

Wrócę
źródło

Odpowiedzi:

8

Dopasowywanie gotówki

Jest to problem z dopasowaniem gotówki. Możesz to śledzić na jednym z dwóch poziomów:

  • Porównaj faktury z danymi liczbowymi w gotówce (nieco niechlujna, ale w rzeczywistości tak robi się w przypadku transakcji wewnętrznych przez większość syndykatów Lloyda, często nazywanych raportem „pisemnym kontra podpisanym”).

  • Zachowaj wyraźne alokacje środków pieniężnych z płatności gotówkowych w podziale na faktury.

Z twojego pytania myślę, że chcesz zrobić to drugie.

Zazwyczaj odbywa się to poprzez oddzielny zestaw transakcji gotówkowych i tabelę pomostową, która ma przydział płatności gotówkowych do faktur. Jeśli wartości są równe lub płatność gotówkowa zawiera jeden numer faktury, możesz dokonać alokacji automatycznie. Jeśli istnieje faktura M: M między fakturami a płatnościami, będziesz musiał wykonać proces ręcznego dopasowywania (automatyczne wykonanie jest w rzeczywistości wariantem problemu plecakowego ).

Podstawowy system uzgadniania gotówki

Wyobraź sobie, że masz tabelę faktur, tabelę płatności gotówkowych i tabelę alokacji. Gdy wystawiasz fakturę, ustawiasz rekord faktury w tabeli faktur oraz rekord „należności” lub „zobowiązań” w tabeli alokacji.

  • Faktura nr 1, 100 USD

  • Alokacja: rekord z odniesieniem do faktury nr 1, rodzaju transakcji „należnej” i kwoty 100 USD. Brak odniesienia do płatności gotówkowej w tym rekordzie.

Teraz otrzymasz płatność gotówkową w wysokości 100 USD

  • Płatności gotówkowe (nr 12345): 100 USD

  • Alokacja: rekord z odniesieniem do faktury nr 1 i numeru 12345, typ transakcji „gotówkowej” i należność w wysokości -100 (zapłacono 100 USD).

Możesz to uogólnić na relację M: M, w której otrzymujesz wiele płatności na podstawie jednej faktury lub płatności obejmującej wiele faktur. Ta struktura ułatwia także tworzenie raportów kontroli kredytowej. Raport musi tylko znaleźć faktury starsze niż (powiedzmy) 180 dni, które wciąż mają zaległe salda.

Oto przykład schematu oraz kilka scenariuszy i przestarzałe zapytanie dotyczące zadłużenia. Niestety nie mam pod ręką uruchomionej instancji mysql, więc ta jest przeznaczona dla programu 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
źródło
Mój ma osobne tabele dla faktur i płatności. Możesz użyć wspólnej tabeli z wewnętrznym powiązaniem. Uzupełnianie gotówki jest często wdrażane w ten sposób w systemach księgowych.
ConcernedOfTunbridgeWells
Udało mi się przetłumaczyć twój przykład SQL Servera na MySQL. Przeszedłem i teraz bardzo dobrze rozumiem. Co by AllocationTypebyło, gdybym chciał wysłać pieniądze klienta? Czy muszę również wstawiać do CashPaymenttabeli (powiedzmy, płacąc je za pośrednictwem BACS)?
Wrócę
1
Tak, chcesz mieć zapis płatności gotówkowej dla płatności przychodzących i wychodzących. Rzeczywiste typy transakcji dla transakcji uzgadniania gotówki zależą od Ciebie.
ConcernedOfTunbridgeWells
1
Jeśli chcesz, możesz dopasować transakcje z faktur w obu kierunkach do jednej płatności rozliczeniowej. Na przykład: Faktura wychodząca za 100 USD, faktura przychodząca za 50 USD (-50) i płatność bilansująca przychodząca za 50 USD dopasowane do obu faktur.
ConcernedOfTunbridgeWells