#python #mysql #django #erp
#python #mysql #django #erp
Вопрос:
Справочная информация: инструмент ERP, который будет содержать всю информацию о транзакциях, такую как начальный баланс и сумма транзакции, и конечный баланс пользователя. Начальный баланс текущей строки зависит от конечного баланса предыдущей строки. Итоговый баланс рассчитывается на основе сложения или вычитания начального баланса и суммы транзакции. Этот вычисленный баланс закрытия будет использоваться в качестве начального баланса следующей строки и так далее.
Примечание: информация о транзакции, такая как сумма транзакции, поступает из покупок продуктов, которые есть на складе
Проблема: считайте, что у меня есть 100 записей. По какой-то причине администратор хочет изменить сумму транзакции 1-й записи. Из-за этого мой конечный баланс первой строки изменится. Но проблема в том, что все остальные 99 строк зависят от баланса закрытия первой строки. Как создать таблицу SQL, которая решает эту проблему с данными в зависимости от столбца.
PS: Я использую Django в качестве фреймворка, но необработанный SQL-запрос и объяснение также в некоторой степени решат мою проблему.
Ответ №1:
Вы действительно должны опубликовать свои модели…
Но учитывая базовую структуру, подобную этой:
from django.db import models, transaction
from django.utils import timezone
class Account(models.Model):
iban = models.CharField(max_length=34, primary_key=True)
@property
def balance(self):
return self.mutations.last().end
class AccountMutation(models.Model):
id = models.BigAutoField(primary_key=True)
account = models.ForeignKey(
Account, on_delete=models.PROTECT, related_name="mutations"
)
start = models.DecimalField(decimal_places=4, max_digits=12)
timestamp = models.DateTimeField(default=timezone.now)
amount = models.DecimalField(decimal_places=4, max_digits=12)
end = models.DecimalField(decimal_places=4, max_digits=12)
objects = AccountMutationManager()
class Meta:
ordering = ("timestamp", "pk")
Мы можем реализовать пользовательский менеджер следующим образом:
class AccountMutationManager(models.Manager):
@transaction.atomic
def recalculate(self, from_: "AccountMutation"):
qs = self.filter(
account=from_.account, timestamp__gte=from_.timestamp, id__gt=from_.id
).select_for_update()
prev = from_
for mutation in qs:
mutation.start = prev.end
mutation.end = mutation.start mutation.amount
mutation.save()
prev = mutation
Конечно, это основано на мутациях на основе временных меток для определения порядка. Если вы связываете транзакции с предыдущим указателем, выбор немного отличается, и вам нужен метод reorder() в случае изменения метки времени. Но это действительно зависит от того, как он хранится.
Как правило, я бы не сохранял начальный и конечный баланс с мутациями, а делал бы их вычисляемыми полями, и каждая учетная запись имела бы начальный баланс для каждого «финансового года», с которого вы начинаете вычисления.
Некоторые пояснения:
- Мы используем подход «все или ничего» с атомарными транзакциями
- кроме того, мы блокируем все строки,
select_for_update()
которые будут затронуты, чтобы одновременно не могли выполняться два пересчета, которые влияют на одни и те же данные. from_
была ли изменена мутация и предполагается, что она имеет правильный конечный баланс