Как отслеживать стоимость продукта с течением времени для POS-системы?

Я разрабатываю базу данных «POS» для точек продаж, используя MySQL в качестве моей СУБД.

При добавлении продуктов в базу продуктов я добавляю название продукта (например, "Coca-Cola 2.L") и код UPC, стоимость, цену, отдел, поставщика и количество.

Итак, если я получу заказ на 1000 бутылок, который обойдется мне в 1 доллар за бутылку

Затем, скажем, через 2 месяца у меня осталось всего 100 бутылок на складе «моя цена на них составляет 100 долларов». Теперь я заказал еще 5000 бутылок, но на этот раз, поскольку я заказал 5000, поставщик дает мне скидку 0,10 с каждой бутылки (т. е. стоимость каждой 0,90 доллара). Итак, стоимость моего второго заказа составляет 4500 долларов. Я хочу убедиться, что очень точно отслеживаю свою прибыль. Поэтому я хочу иметь возможность отслеживать 100 заказов, которые я купил по цене 1 доллар, отдельно по цене 1 доллар и новый заказ по цене 0,90 доллара.

То, как я в настоящее время отслеживаю стоимость каждого предмета продажи, считывая текущую стоимость из таблицы продуктов и сохраняя ее в таблице sales_transactions_items вместе с ценой продажи, идентификатором транзакции и идентификатором продукта.

Проблема, с которой я столкнулся сейчас, заключается в том, что, когда я получил бутылку 5000, я изменил эту стоимость с 1 доллара на 0,90 доллара, что увеличило мою прибыль (100 бутылок x 0,10 доллара экономии на бутылку) 100x0,10 = 10 долларов прибыли, которую я сделал. фактически не заработал. После того, как количество продано, это несоответствие возникло из-за того, что значение количества достигло 5100 бутылок, из которых 100 были куплены по 1 доллару США, а 5000 были куплены по 0,90 доллара каждая.

Мой вопрос: как мне решить эту проблему, когда я действительно могу определить, сколько мне стоил каждый предмет.


person Jaylen    schedule 10.01.2015    source источник
comment
Вам нужен другой стол. Один для продуктов, другой для покупок.   -  person Strawberry    schedule 10.01.2015
comment
как это может помочь мне определить, что первые 1000 были куплены по 1 доллару, а 5000 — по 0,90 доллара? item_id и код UPC все те же   -  person Jaylen    schedule 10.01.2015
comment
Потому что вы будете хранить оба события — product_id, date, unit_price (или total) и количество.   -  person Strawberry    schedule 10.01.2015
comment
Мне до сих пор не ясно, как это решит проблему. Не могли бы вы помочь мне понять решение больше? Буду признателен за подробный ответ.   -  person Jaylen    schedule 10.01.2015
comment
Запишите свои покупки с датой/временем, стоимостью, количеством приобретенных товаров и количеством непроданных товаров в таблице. Запишите свои продажи с указанием даты/времени, количества товаров и стоимости этих товаров. Вы, вероятно, захотите запустить систему FIFO, чтобы сначала продавать самые старые товары. Если вы продаете 300 товаров, 100 из них по старой цене и 200 по новой цене, вы записываете две строки товаров для продажи, по одной для каждой цены покупки. Если предметы отслеживаются индивидуально (имеют серийный номер — в отличие от обычной бутылки кока-колы, но очень похоже на обычный компьютер), то вы снова действуете по-другому.   -  person Jonathan Leffler    schedule 11.01.2015
comment
@JonathanLeffler спасибо за объяснение. Последнее, что мне нужно понять, это как обеспечить, чтобы значение столбца number оставалось › -1. Кроме того, в случае двух транзакций, выполняемых в одно и то же время, мне нужно как-то сказать MySQL, чтобы заблокировать таблицу, пока транзакция не будет завершена.   -  person Jaylen    schedule 12.01.2015
comment
Обычно я применяю ограничение к столбцу: CHECK(number >= 0). Это более или менее стандартный SQL (концепция стандартная; мне нужно просмотреть нотацию), но я не знаю, поддерживает ли MySQL объявление и принудительное применение таких ограничений. Насчет одновременного обновления: для этого и нужны транзакции и СУБД. Если вы используете соответствующий движок (я полагаю, InnoDB для MySQL), то СУБД обрабатывает это автоматически. Вы выполняете все операции модификации для продажи в одной транзакции, а СУБД предотвращает взаимодействие транзакций друг с другом.   -  person Jonathan Leffler    schedule 12.01.2015
comment
@JonathanLeffler Я работаю над этим. Я дам вам знать, если что-то произойдет.   -  person Jaylen    schedule 13.01.2015
comment
@JonathanLeffler Большое спасибо. Как я буду обрабатывать возвраты? Как я узнаю, к какому идентификатору покупки я добавлю товары?   -  person Jaylen    schedule 15.01.2015
comment
Есть разные возможности — ни одна из них не тривиальна. Если у вас есть номер квитанции, вы можете найти его в таблице продаж, и обычно там указывается цена — если детали счета/квитанции достаточно подробны, вы можете определить, какой лот по цене соответствует цене. продажа принадлежала. Понятно, что если в квитанции указана разделенная партия, вы должны выбрать, из какой партии поступил возвращаемый товар. Это чем раньше, чем позже, тем дешевле, тем дороже партия? Если у вас нет квитанции или сведения о квитанции неадекватны, вы, вероятно, в конечном итоге угадаете, из какой партии она пришла.   -  person Jonathan Leffler    schedule 15.01.2015
comment
В квитанции будет указана совокупная стоимость и количество. Таким образом, количество товара 1 = 150 товаров по цене 145 долларов, поэтому, если клиент хочет вернуть 10 из 150, как я узнаю стоимость 10, которые я хочу вернуть, и в какое ведро вернуть?   -  person Jaylen    schedule 15.01.2015


Ответы (2)


Вот достаточно общий, но упрощенный пример того, как может выглядеть ваша схема.

CREATE TABLE products
(
  `id` int not null auto_increment primary key, 
  `name` varchar(13), 
  `price` decimal(12, 2), -- current sale price. You might want to extract it into it's own table `prices`
  ...
);

CREATE TABLE orders
(
  `id` int not null auto_increment primary key, 
  `date` date,
  ...
);

CREATE TABLE order_items
(
  `id` int not null auto_increment primary key, 
  `order_id` int not null, 
  `product_id` int, 
  `quantity` decimal(12, 3), 
  `cost` decimal(12, 2),
  foreign key (`order_id`) references orders (id),
  foreign key (`product_id`) references products (id)
);

CREATE TABLE sales
(
  `id` int not null auto_increment primary key, 
  `date` datetime,
  ...
);

CREATE TABLE sale_items
(
  `id` int not null auto_increment primary key, 
  `sale_id` int not null, 
  `product_id` int, 
  `quantity` decimal(12, 3), 
  `price` decimal(12, 2),
  foreign key (`sale_id`) references sales (id),
  foreign key (`product_id`) references products (id)
);

Вот демонстрация SQLFiddle.

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


Один из способов расчета общего объема продаж, общей фактической себестоимости и маржи по продукту.

SELECT product_id, p.name, sales_quantity, sales_total, cost_total, sales_total - cost_total margin
  FROM
(
  SELECT product_id, sales_quantity, sales_total, SUM(
    CASE WHEN sales_quantity >= running_quantity 
           THEN cost * quantity
         WHEN sales_quantity BETWEEN running_quantity - quantity AND running_quantity
           THEN cost * (sales_quantity - (running_quantity - quantity))
          ELSE 0 
     END) cost_total
    FROM
  (
    SELECT s.*, o.cost, o.quantity, o.running_quantity
      FROM
    (
      SELECT product_id, 
             SUM(quantity * price) sales_total, 
             SUM(quantity) sales_quantity
      FROM sale_items
     GROUP BY product_id
    ) s JOIN 
    (
      SELECT product_id, cost, quantity, (
        SELECT SUM(quantity)
          FROM order_items
         WHERE product_id = i.product_id
           AND order_id <= i.order_id
         ) running_quantity
        FROM order_items i
    ) o
        ON s.product_id = o.product_id
  ) q
   GROUP BY product_id, sales_quantity, sales_total
) q JOIN products p
    ON q.product_id = p.id

Пример вывода:

| PRODUCT_ID |          NAME | SALES_QUANTITY | SALES_TOTAL | COST_TOTAL | MARGIN |
|------------|---------------|----------------|-------------|------------|--------|
|          1 | Coca-Cola 2.L |            150 |       187.5 |        145 |   42.5 |

Вот демонстрация SQLFiddle.

Вы можете видеть, что в примере первые 100 бутылок из 150 проданных стоят 100 долларов (1 доллар * 100), а остальные 50 - 45 долларов (0,9 * 50).

person peterm    schedule 10.01.2015
comment
Я до сих пор не понимаю, как этот дизайн поможет мне определить стоимость каждого заказа. поэтому я продал 2 предмета product_id = 1, один стоил мне 1 доллар, а другой стоил мне 0,9 доллара. Как эта настройка может помочь мне определить, что моя стоимость составляет 1,90 доллара? Я вижу, что вы фиксируете покупку / заказ отдельно, но как я могу определить стоимость на основе этого? Как бы я написал запрос, чтобы определить нашу истинную стоимость? - person Jaylen; 10.01.2015
comment
ЕСЛИ вы добавите поле стоимости в sale_items, вы сможете распределить две или более стоимости на элемент. См. FIDDLE. - person david strachan; 11.01.2015
comment
значение цены должно быть легко доступно, так как оно хранится в таблице продуктов. Но как бы вы вычислили значение cost перед вставкой новой записи в таблицу sale_item? - person Jaylen; 11.01.2015
comment
@Mike Это как-то помогло? - person peterm; 12.01.2015
comment
@peterm да, это так. Я все еще работаю над этим, пытаясь найти лучший подход к этой проблеме. Я очень благодарен за ваш ответ и помощь, но я стараюсь избегать написания такого сложного запроса, если только я этого не сделал. Я пытаюсь следовать приведенному выше предложению Джонатана Леффлера, что позволит мне отслеживать стоимость на уровне транзакции, а также позволит мне узнать, сколько товаров осталось с их стоимостью, используя систему FIFO. - person Jaylen; 13.01.2015

«Я хочу убедиться, что я очень точно отслеживаю свою прибыль. Поэтому я хочу, чтобы 100, которые я купил по цене 1 доллар, могли отслеживать их отдельно по цене 1 доллар и новый заказ по цене 0,90 доллара». - Это не обязательно. Вы должны рассчитать текущую среднюю цену и по ней рассчитать прибыль. У вас нет серийных номеров для каждой бутылки, и по этой причине, даже если вы хотите узнать прибыль по каждому предмету, вы не можете этого сделать. И с точки зрения владельца, важно знать общую прибыль от Coca Cola, а не то, что "с этой бутылки 0,50, а с той бутылки 0,45".

person i486    schedule 08.07.2016