Gerando faturas e rastreamento

11

A cada 2 semanas, o sistema gera as faturas para as empresas.

A empresa receberá uma fatura nos dias 1 e 16 todos os meses. (Ele será executado via Cron Job a cada 2 semanas. Ele digitaliza a tabela de pedidos e depois é adicionada à tabela 'fatura'. Existe alternativa?)

Há uma lista de pedidos de clientes na orderstabela e também indica a qual empresa pertence ( orders.company_id)

A invoicetabela calcula o custo total dos pedidos da orderstabela.

Estou tentando descobrir como criar um rastreamento razoável de faturas. Em algum momento, a empresa terá que me enviar as taxas ou em algum momento eu as envio ( invoice.amount)

Preciso acompanhar as faturas com o seguinte:

  • quando a empresa me enviou o valor
  • quando enviei o valor para a empresa
  • quanto foi recebido da empresa
  • quanto eu enviei para a empresa
  • recebi o valor total (caso contrário, o que preciso atualizar no banco de dados?)
  • status da fatura (fatura enviada, cancelada, valor recebido, valor enviado)

Aqui está o design do banco de dados que eu criei:

mesa da empresa

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

Os clientes podem selecionar uma empresa no meu site.

tabela de pedidos

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

dois clientes encomendaram os produtos da empresa B ( orders.company_id = 2). Eu sei que os campos de pedidos não são suficientes, apenas simplificados para você.

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

Lista de produtos solicitados pelos clientes.

tabela de faturas

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

É aqui que estou bastante atolado no design das tabelas de faturas. Não tenho certeza de como isso deve ser feito. As faturas serão geradas a cada 2 semanas. No exemplo do resultado invoice.amounté 55,00 porque foi calculado a partir da orders.company_id = 2tabela

Se o valor invoice.amountfor -50,00 (menos), significa que a empresa precisará me enviar o valor das taxas.

Se o valor invoice.amountfor 50,00, significa que preciso enviar as taxas à empresa.

O status_id pode ser: (1) Fatura enviada, (2) Cancelada, (3) Concluída

Preciso adicionar invoice_idcampo na orderstabela? Atualize o orders.invoice_idcampo quando a linha tiver sido inserida na tabela 'fatura'.

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

É aqui que eu posso rastrear e atualizar a transação. O pagamento será feito via BACS.

Este projeto de boas tabelas ou o que eu preciso melhorar? Quais campos e tabelas devo adicionar?

Se a fatura foi gerada e, posteriormente, preciso fazer alterações orders_productsou orderstabelas - ela deve recalcular o invoice.amountcampo? (Eu vou estar usando PHP / MySQL).

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

Sinta-se à vontade para atualizar / adicionar tabelas à resposta aqui.

obrigado

Eu voltarei
fonte

Respostas:

8

Correspondência em dinheiro

Este é um problema de correspondência de dinheiro. Você pode acompanhar isso em um dos dois níveis:

  • Compare os números faturados com os valores em dinheiro (um tanto desleixados, mas é assim que a maioria dos sindicatos do Lloyd's realiza, para os negócios internos, geralmente chamada de relatório 'escrito versus assinado').

  • Manter alocações de caixa explícitas a partir de pagamentos em dinheiro discriminados por fatura.

Da sua pergunta, acho que você quer fazer o último.

Normalmente, isso é feito com um conjunto separado de transações em dinheiro e uma tabela de ponte que possui a alocação de pagamentos em dinheiro às faturas. Se os valores forem iguais ou o pagamento em dinheiro vier com uma única referência à fatura, você poderá fazer a alocação automaticamente. Se houver uma relação M: M entre faturas e pagamentos, você precisará fazer um processo de correspondência manual (fazer isso automaticamente é na verdade uma variante do problema da mochila ).

Um sistema básico de correspondência monetária

Imagine que você tenha uma tabela de faturas, uma tabela de pagamentos em dinheiro e uma tabela de alocação. Ao emitir uma fatura, você configura um registro de fatura na tabela de faturas e um registro 'a receber' ou 'a pagar' na tabela de alocações.

  • Fatura nº 1, US $ 100

  • Alocação: um registro com referência à fatura nº 1, tipo de transação 'a receber' e $ 100 devidos. Nenhuma referência a um pagamento em dinheiro neste registro.

Agora, você recebe um pagamento em dinheiro de US $ 100

  • Pagamentos em dinheiro (chq # 12345): US $ 100

  • Alocação: um registro com uma referência à fatura nº 1 e chq nº 12345, tipo de transação 'em dinheiro' e -100 devido (US $ 100 pagos).

Você pode generalizar isso para um relacionamento M: M em que você recebe vários pagamentos em uma única fatura ou um pagamento que cobre várias faturas. Essa estrutura também facilita bastante a criação de relatórios de controle de crédito. O relatório só precisa encontrar faturas anteriores a (digamos) 180 dias que ainda possuem saldos pendentes.

Aqui está um exemplo do esquema, além de alguns cenários e uma consulta de dívida antiga. Infelizmente não tenho uma instância do mysql em execução, então esta é para o 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
fonte
A minha possui tabelas separadas para faturas e pagamentos. Você poderia usar uma tabela comum com ligação interna. A correspondência de caixa geralmente é implementada dessa maneira nos sistemas contábeis.
ConcernedOfTunbridgeWells
Consegui traduzir seu exemplo do SQL Server para o MySQL. Eu passei e entendi muito bem agora. O que AllocationTypeseria se eu quiser enviar dinheiro ao cliente? Também preciso inserir na CashPaymenttabela (digamos que pagá-los via BACS)?
Vou-Be-Back
1
Sim, você deseja um registro de pagamento à vista para pagamentos a receber e a pagar. Os tipos de transação reais para as transações correspondentes à caixa são com você.
ConcernedOfTunbridgeWells
1
Você pode comparar transações de faturas em ambas as direções com um único pagamento de liquidação, se desejar. Por exemplo: fatura de saída de US $ 100, fatura de entrada de US $ 50 (-50) e pagamento de saldo de entrada de US $ 50 correspondente às duas faturas.
ConcernedOfTunbridgeWells