Генерация счетов и отслеживание


11

Каждые 2 недели система будет генерировать счета для компаний.

Компания будет получать счета 1-го и 16-го числа каждого месяца. (Он запускается через Cron Job каждые 2 недели. Он просматривает таблицу заказов, а затем добавляет в таблицу счетов-фактур. Есть ли альтернатива?)

В ordersтаблице приведен список заказов клиентов, а также указано, к какой компании он принадлежит ( orders.company_id)

По invoiceтаблице рассчитывают общую стоимость заказов из ordersтаблицы.

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

Мне нужно отслеживать счета со следующим:

  • когда компания прислала мне сумму
  • когда я отправил сумму в компанию
  • какая сумма была получена от компании
  • сколько я отправил в компанию
  • я получил полную сумму (если нет, что мне нужно обновить на БД?)
  • статус счета (отправленный счет, отмененный, полученная сумма, отправленная сумма)

Вот дизайн базы данных, который я придумал:

корпоративный стол

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). Я знаю, что поля заказов недостаточно, просто для вас это упрощено.

таблица orders_products

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поле, когда строка была вставлена ​​в таблицу счетов-фактур.

таблица invoice_payment

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

Соответствие денежных средств

Это проблема совпадения денежных средств. Вы можете отслеживать это на одном из двух уровней:

  • Сравните счета-фактуры с денежными цифрами (несколько небрежно, но на самом деле именно так и делается для внутреннего бизнеса большинство Синдикатов Ллойда, часто называемое «письменный или подписанный» отчет).

  • Сохраняйте явные денежные ассигнования из наличных платежей в разбивке по счетам.

Из вашего вопроса я думаю, что вы хотите сделать последнее.

Как правило, это делается с помощью отдельного набора транзакций с наличными деньгами и промежуточной таблицы, в которой размещены платежи наличными по счетам. Если значения равны или оплата наличными идет с одной ссылкой на счет, вы можете выполнить распределение автоматически. Если между счетами и платежами существует отношение M: M, вам нужно будет выполнить процесс сопоставления вручную (выполнение этого автоматически является вариантом проблемы с рюкзаком ).

Базовая система сопоставления денежных средств

Представьте, что у вас есть таблица счетов, таблица платежей наличными и таблица распределения. Когда вы выставляете счет-фактуру, вы настраиваете запись счета-фактуры в таблице счетов-фактур и запись «дебиторская задолженность» или «подлежащая оплате» в таблице ассигнований.

  • Счет № 1, $ 100

  • Распределение: запись со ссылкой на счет № 1, тип транзакции «дебиторская задолженность» и задолженность в размере 100 долларов США. Нет ссылки на наличный платеж в этой записи.

Теперь вы получаете наличный расчет в размере 100 долларов

  • Наличные платежи (chq # 12345): $ 100

  • Распределение: запись со ссылкой на счет-фактуру № 1 и чк № 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

У меня есть отдельные таблицы для счетов и платежей. Вы можете использовать общую таблицу с внутренней связью. Соответствие денежных средств часто применяется таким образом в учетных системах.
ConcernedOfTunbridgeWells

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

1
Да, вам нужна запись о наличных платежах как для входящих, так и для исходящих платежей. Фактические типы транзакций для транзакций сопоставления денежных средств зависят от вас.
ConcernedOfTunbridgeWells

1
Вы можете сопоставить транзакции из счетов в обоих направлениях с одним расчетным платежом, если хотите. Например: исходящий счет на 100 долларов, входящий счет на 50 долларов (-50) и балансировка входящего платежа на 50 долларов, сопоставленная с обоими счетами.
ConcernedOfTunbridgeWells
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.