Генерування рахунків-фактур та відстеження


11

Кожні 2 тижні система генерує рахунки для компаній.

Компанія отримуватиме рахунок 1-го та 16-го щомісяця. (Він працюватиме через Cron Job кожні 2 тижні. Він сканує таблицю замовлень, а потім додає до таблиці "рахунок-фактура". Чи є альтернатива?)

У ordersтаблиці є список замовлень клієнтів, а також вказується, якій компанії належить ( orders.company_id)

У invoiceтаблиці обчислюють загальну вартість замовлень з ordersтабл.

Я намагаюся зрозуміти, як розробити розумне відстеження рахунків. Колись компанія повинна надіслати мені гонорари або колись я надішлю їм збори ( invoice.amount)

Мені потрібно відстежувати рахунки-фактури з наступним:

  • коли компанія надіслала мені суму
  • коли я надіслав цю суму компанії
  • скільки сума надійшла від компанії
  • скільки я надіслав компанії
  • я отримав повну суму (якщо ні, що мені потрібно оновити на Db?)
  • стан рахунка-фактури (рахунок-фактура надіслано, скасовано, отримана сума, надіслана сума)

Ось дизайн бази даних, який я придумав:

стіл компанії

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

Клієнти можуть вибрати компанію з мого веб-сайту.

таблиця замовлень

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

два замовники замовили продукцію у компанії B ( orders.company_id = 2). Я знаю, що полів замовлень недостатньо, просто спрощено для вас.

таблиця замовлень_продуктів

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

Перелік продуктів, які замовили клієнти.

таблиця рахунків-фактур

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

Ось де я сильно застряг у дизайні таблиць накладних. Я не впевнений, як це робити. Рахунки будуть формуватися кожні 2 тижні. З результату приклад invoice.amount55,00, оскільки він був обчислений з orders.company_id = 2табл

Якщо invoice.amountдорівнює -50,00 (мінус), це означає, що компанія повинна надіслати мені суму гонорару.

Якщо invoice.amount50,00, це означає, що мені потрібно надіслати компанії збори.

Status_id може бути: (1) Надісланий рахунок-фактура, (2) Скасовано, (3) Завершено

Чи потрібно додати invoice_idполе в ordersтаблицю? Оновіть orders.invoice_idполе, коли рядок було вставлено у таблицю "рахунок-фактура".

рахунок-оплата таблиці

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

Тут я можу відстежувати та оновлювати транзакції .. оплата буде здійснена через BACS.

Це хороший дизайн столів чи що мені потрібно вдосконалити? Які поля та таблиці я повинен додати?

Якщо рахунок-фактура був сформований і пізніше мені потрібно внести зміни orders_productsабо ordersтаблиці - чи слід перерахувати invoice.amountполе? (Я буду використовувати PHP / MySQL).

Дамп 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');

Сміливо бажаєте оновити / додати таблиці до відповіді тут.

Спасибі

Відповіді:


8

Грошовий збір

Це проблема зіставлення грошових коштів. Ви можете відстежити це на одному з двох рівнів:

  • Порівняйте рахунок-фактуру з цифрами грошових коштів (дещо неохайний, але це насправді так, як це робиться для внутрішнього бізнесу більшістю Lloyd's Syndicate, що часто називається звітом "письмовий проти підписаного").

  • Зберігайте чіткі грошові асигнування з готівкових платежів, розбитих за рахунками.

З вашого запитання я думаю, що ви хочете зробити останнє.

Зазвичай це робиться за допомогою окремого набору касових операцій та мостової таблиці, що містить розподіл грошових платежів за рахунками-фактурами. Якщо значення рівні або платіж готівкою здійснюється з однією довідкою рахунку-фактури, ви можете зробити розподіл автоматично. Якщо між рахунками та платежами існує взаємозв'язок M: M, вам потрібно буде виконати процедуру узгодження вручну (це автоматично автоматично є фактично варіантом проблеми з рюкзаком ).

Основна система узгодження грошових коштів

Уявіть, що у вас є таблиця рахунків-фактур, таблиця платежів з готівкою та таблиця розподілу. Коли ви видаєте рахунок-фактуру, тоді ви встановлюєте запис рахунків-фактур в таблиці рахунків-фактур, а запис "дебіторська заборгованість" або "підлягає сплаті" в таблиці розподілів.

  • Рахунок №1, 100 дол

  • Розподіл: запис із посиланням на рахунок-фактура №1, тип транзакції "дебіторська заборгованість" та 100 дол. На цьому записі немає посилання на платіж готівкою.

Тепер ви отримуєте грошову виплату в розмірі 100 доларів

  • Грошові виплати (chq # 12345): 100 дол

  • Виділення: запис із посиланням на рахунок-фактура №1 та chq # 12345, тип транзакції "готівкою" та -100 заборгованість (100 доларів США).

Ви можете узагальнити це на відносини M: M, де ви отримуєте кілька платежів за один рахунок-фактуру або платіж, що покриває кілька рахунків-фактур. Ця структура також спрощує створення звітів про кредитний контроль. У звіті потрібно знайти рахунки-фактури, старші (скажімо, на 180 днів), які ще мають залишки.

Ось приклад схеми плюс пара сценаріїв та запит щодо застарілого боргу. На жаль, у мене не працює запущений екземпляр mysql, тому для 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

У шахти є окремі таблиці для виставлення рахунків та платежів. Ви можете використовувати загальну таблицю з внутрішнім зв’язком. Відповідність грошових коштів часто реалізується саме таким чином у системах обліку.
Занепокоєний

Мені вдалося перевести ваш приклад SQL Server у MySQL. Я пройшов, і зараз дуже добре зрозумів. Що було AllocationTypeб, якщо я хочу надіслати гроші клієнту? Чи потрібно мені також вставляти в CashPaymentтаблицю (скажімо, оплачувати їх через BACS)?
Я повернусь

1
Так, ви хочете записати готівкові платежі як для вхідних, так і для вихідних платежів. Ви визначаєте фактичні типи транзакцій для транзакцій, що відповідають грошовим коштам.
ЗанепокоєнняTunbridgeWells

1
Якщо хочете, ви можете зіставити транзакції за рахунками в обох напрямках проти одного розрахункового платежу. Наприклад: вихідний рахунок-фактура за 100 доларів, вхідний рахунок-фактура за 50 доларів США (-50) та врівноважувальний платіж, що надходить за 50 доларів, збігаються з обома рахунками.
ЗанепокоєнняTunbridgeWells
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.