task_invoice.py 30.6 KB
Newer Older
1
# -*- coding: utf-8 -*-
Patrick's avatar
Patrick committed
2
import threading
Patrick's avatar
Patrick committed
3

4
from django.contrib import messages
Patrick's avatar
Patrick committed
5
from django.db.models import Sum
6
from django.utils.translation import ugettext_lazy as _
Patrick's avatar
Patrick committed
7 8 9

import repanier.apps
from repanier.email import email_invoice
10 11 12
from repanier.models import BankAccount
from repanier.models import Customer
from repanier.models import CustomerInvoice
Patrick's avatar
Patrick committed
13
from repanier.models import OfferItem
Patrick's avatar
Patrick committed
14
from repanier.models import CustomerProducerInvoice
15 16 17
from repanier.models import Permanence
from repanier.models import Producer
from repanier.models import ProducerInvoice
Patrick's avatar
Patrick committed
18
from repanier.models import Product
19
from repanier.models import Purchase
Patrick's avatar
Patrick committed
20
from repanier.models import LUT_DeliveryPoint
21 22 23 24 25
from repanier.task import task_producer
from repanier.tools import *


@transaction.atomic
Patrick's avatar
Patrick committed
26
def generate_invoice(permanence, payment_date):
27
    getcontext().rounding = ROUND_HALF_UP
28 29
    from repanier.apps import REPANIER_SETTINGS_MEMBERSHIP_FEE, REPANIER_SETTINGS_MEMBERSHIP_FEE_DURATION
    today = timezone.now().date()
Patrick's avatar
Patrick committed
30 31 32 33 34
    bank_account = BankAccount.objects.filter(operation_status=BANK_LATEST_TOTAL).order_by('?').first()
    producer_buyinggroup = Producer.objects.filter(represent_this_buyinggroup=True).order_by('?').first()
    customer_buyinggroup = Customer.objects.filter(represent_this_buyinggroup=True).order_by('?').first()
    if bank_account is None or producer_buyinggroup is None or customer_buyinggroup is None:
        return
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    customer_invoice_buyinggroup = CustomerInvoice.objects.filter(
        customer_id=customer_buyinggroup.id,
        permanence_id=permanence.id,
    ).order_by('?').first()
    if customer_invoice_buyinggroup is None:
        customer_invoice_buyinggroup = CustomerInvoice.objects.create(
            customer_id=customer_buyinggroup.id,
            permanence_id=permanence.id,
            date_previous_balance=customer_buyinggroup.date_balance,
            previous_balance=customer_buyinggroup.balance,
            date_balance=payment_date,
            balance=customer_buyinggroup.balance,
            customer_charged_id=customer_buyinggroup.id,
            transport=repanier.apps.REPANIER_SETTINGS_TRANSPORT,
            min_transport=repanier.apps.REPANIER_SETTINGS_MIN_TRANSPORT,
            price_list_multiplier=DECIMAL_ONE
        )
Patrick's avatar
Patrick committed
52
    old_bank_latest_total = bank_account.bank_amount_in.amount - bank_account.bank_amount_out.amount
Patrick's avatar
Patrick committed
53 54 55 56 57 58
    permanence_partially_invoiced = ProducerInvoice.objects.filter(
        permanence_id=permanence.id,
        invoice_sort_order__isnull=True,
        to_be_paid=False
    ).order_by('?').exists()
    if permanence_partially_invoiced:
59
        # Move the producers not invoiced into a new permanence
Patrick's avatar
Patrick committed
60 61 62 63 64 65 66 67 68 69
        producers_to_keep = list(Producer.objects.filter(
            producer_invoice__permanence_id=permanence.id,
            producer_invoice__invoice_sort_order__isnull=True,
            producer_invoice__to_be_paid=True).only('id').order_by('?'))
        permanence.producers.clear()
        permanence.producers.add(*producers_to_keep)
        producers_to_move = list(Producer.objects.filter(
            producer_invoice__permanence_id=permanence.id,
            producer_invoice__invoice_sort_order__isnull=True,
            producer_invoice__to_be_paid=False).only('id').order_by('?'))
70
        new_permanence = permanence.create_child(PERMANENCE_SEND)
Patrick's avatar
Patrick committed
71
        new_permanence.producers.add(*producers_to_move)
Patrick's avatar
Patrick committed
72 73
        ProducerInvoice.objects.filter(
            permanence_id=permanence.id,
74
            producer_id__in=producers_to_move
Patrick's avatar
Patrick committed
75 76 77 78 79
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )
        CustomerProducerInvoice.objects.filter(
            permanence_id=permanence.id,
80
            producer_id__in=producers_to_move
Patrick's avatar
Patrick committed
81 82 83 84 85
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )
        OfferItem.objects.filter(
            permanence_id=permanence.id,
86
            producer_id__in=producers_to_move
Patrick's avatar
Patrick committed
87 88 89 90
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )

91 92 93 94 95 96 97 98
        for purchase in Purchase.objects.filter(
            permanence_id=permanence.id,
            producer_id__in=producers_to_move
        ).order_by().distinct('customer_invoice'):
            customer_invoice = CustomerInvoice.objects.filter(
                id=purchase.customer_invoice_id
            ).order_by('?').first()
            new_customer_invoice = customer_invoice.create_child(new_permanence=new_permanence)
Patrick's avatar
Patrick committed
99

100
            new_customer_invoice.set_delivery(customer_invoice.delivery)
Patrick's avatar
Patrick committed
101
            # Important : The purchase customer charged must be calculated before calculate_and_save_delta_buyinggroup
Patrick's avatar
Patrick committed
102
            Purchase.objects.filter(
103 104
                customer_invoice_id=customer_invoice.id,
                producer_id__in=producers_to_move
Patrick's avatar
Patrick committed
105
            ).order_by('?').update(
106 107
                permanence_id=new_permanence.id,
                customer_invoice_id=new_customer_invoice.id,
Patrick's avatar
Patrick committed
108
            )
Patrick's avatar
Patrick committed
109 110 111
            new_customer_invoice.calculate_and_save_delta_buyinggroup()
            new_customer_invoice.save()

Patrick's avatar
Patrick committed
112
        new_permanence.recalculate_order_amount(re_init=True)
113

114
    for customer_invoice in CustomerInvoice.objects.filter(
Patrick's avatar
Patrick committed
115 116
        permanence_id=permanence.id,
        customer_id=F('customer_charged_id')
117 118
    ).order_by('?'):
        # Need to calculate delta_price_with_tax, delta_vat and delta_transport
Patrick's avatar
Patrick committed
119 120 121 122 123 124 125 126 127
        # Important : A customer may only be responsible of one and only one delivery point
        if customer_invoice.is_group:
            # Refresh in case of change in the admin
            delivery_point = LUT_DeliveryPoint.objects.filter(customer_responsible=customer_invoice.customer).order_by('?').first()
            if delivery_point is not None:
                customer_invoice.price_list_multiplier = delivery_point.price_list_multiplier
                customer_invoice.transport = delivery_point.transport
                customer_invoice.min_transport = delivery_point.min_transport

128 129
        customer_invoice.calculate_and_save_delta_buyinggroup()
        customer_invoice.save()
Patrick's avatar
Patrick committed
130

131 132 133 134
    if REPANIER_SETTINGS_MEMBERSHIP_FEE_DURATION > 0 and REPANIER_SETTINGS_MEMBERSHIP_FEE > 0:
        membership_fee_product = Product.objects.filter(
            order_unit=PRODUCT_ORDER_UNIT_MEMBERSHIP_FEE,
            is_active=True
Patrick's avatar
Patrick committed
135
        ).order_by('?').first()
136 137 138
        membership_fee_product.producer_unit_price = REPANIER_SETTINGS_MEMBERSHIP_FEE
        # Update the prices
        membership_fee_product.save()
139

140
        for customer_invoice in CustomerInvoice.objects.filter(
Patrick's avatar
Patrick committed
141
            permanence_id=permanence.id,
142 143 144 145 146 147 148 149 150
            customer_charged_id=F('customer_id')
        ).select_related("customer").order_by('?'):
            # 4 - Add Membership fee Subscription
            customer = customer_invoice.customer
            if not customer.represent_this_buyinggroup:
                # There is a membership fee
                if customer.membership_fee_valid_until < today:
                    membership_fee_offer_item = get_or_create_offer_item(
                        permanence,
151
                        membership_fee_product
152 153 154 155 156 157 158 159 160 161 162 163 164 165
                    )
                    permanence.producers.add(membership_fee_offer_item.producer_id)
                    create_or_update_one_purchase(
                        customer.id,
                        membership_fee_offer_item,
                        q_order=1,
                        permanence_date=permanence.permanence_date,
                        batch_job=True,
                        is_box_content=False
                    )
                    customer.membership_fee_valid_until = add_months(
                        customer.membership_fee_valid_until,
                        REPANIER_SETTINGS_MEMBERSHIP_FEE_DURATION
                    )
166 167 168 169 170
                    # customer.save(update_fields=['membership_fee_valid_until', ])
                    # use vvvv because ^^^^^ will call "pre_save" function which reset valid_email to None
                    models.Customer.objects.filter(id=customer.id).order_by('?').update(
                        membership_fee_valid_until=customer.membership_fee_valid_until
                    )
171

Patrick's avatar
Patrick committed
172 173
    permanence.recalculate_profit()
    permanence.save()
174

Patrick's avatar
Patrick committed
175 176 177 178 179 180
    for customer_invoice in CustomerInvoice.objects.filter(
        permanence_id=permanence.id
    ):
        customer_invoice.balance = customer_invoice.previous_balance = customer_invoice.customer.balance
        customer_invoice.date_previous_balance = customer_invoice.customer.date_balance
        customer_invoice.date_balance = payment_date
181

Patrick's avatar
Patrick committed
182
        if customer_invoice.customer_id == customer_invoice.customer_charged_id:
Patrick's avatar
Patrick committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
            # ajuster sa balance
            # il a droit aux réductions
            total_price_with_tax = customer_invoice.get_total_price_with_tax().amount
            customer_invoice.balance.amount -= total_price_with_tax
            Customer.objects.filter(
                id=customer_invoice.customer_id
            ).update(
                date_balance=payment_date,
                balance=F('balance') - total_price_with_tax
            )
        else:
            # ne pas modifier sa balance
            # ajuster la balance de celui qui paye
            # celui qui paye a droit aux réductions
            Customer.objects.filter(
                id=customer_invoice.customer_id
            ).update(
                date_balance=payment_date,
            )
        customer_invoice.save()

    # Claculate new stock
    for offer_item in OfferItem.objects.filter(
            is_active=True, manage_replenishment=True, permanence_id=permanence.id
    ).order_by('?'):
        invoiced_qty, taken_from_stock, customer_qty = offer_item.get_producer_qty_stock_invoiced()
        if taken_from_stock != DECIMAL_ZERO:
            if offer_item.price_list_multiplier < DECIMAL_ONE: # or offer_item.is_resale_price_fixed:
                unit_price = offer_item.customer_unit_price.amount
                unit_vat = offer_item.customer_vat.amount
            else:
                unit_price = offer_item.producer_unit_price.amount
                unit_vat = offer_item.producer_vat.amount
            delta_price_with_tax = ((unit_price +
                               offer_item.unit_deposit.amount) * taken_from_stock).quantize(TWO_DECIMALS)
            delta_vat = unit_vat * taken_from_stock
            delta_deposit = offer_item.unit_deposit.amount * taken_from_stock
            producer_invoice = ProducerInvoice.objects.get(
                producer=offer_item.producer,
                permanence=permanence,
223
            )
Patrick's avatar
Patrick committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
            producer_invoice.delta_stock_with_tax.amount -= delta_price_with_tax
            producer_invoice.delta_stock_vat.amount -= delta_vat
            producer_invoice.delta_stock_deposit.amount -= delta_deposit
            producer_invoice.save(update_fields=[
                'delta_stock_with_tax',
                'delta_stock_vat',
                'delta_stock_deposit'
            ])

        # Update new_stock even if no order
        # // xslx_stock and task_invoice
        offer_item.new_stock = offer_item.stock - taken_from_stock + offer_item.add_2_stock
        if offer_item.new_stock < DECIMAL_ZERO:
            offer_item.new_stock = DECIMAL_ZERO
        offer_item.previous_add_2_stock = offer_item.add_2_stock
        offer_item.previous_producer_unit_price = offer_item.producer_unit_price
        offer_item.previous_unit_deposit = offer_item.unit_deposit
        if permanence.highest_status <= PERMANENCE_SEND:
            # Asked by Bees-Coop : Do not update stock when canceling
            new_stock = offer_item.stock if offer_item.stock > DECIMAL_ZERO else DECIMAL_ZERO
            Product.objects.filter(
                id=offer_item.product_id, stock=new_stock
            ).update(stock=offer_item.new_stock)
        offer_item.save()

    for producer_invoice in ProducerInvoice.objects.filter(
            permanence_id=permanence.id):
        producer_invoice.balance = producer_invoice.previous_balance = producer_invoice.producer.balance
        producer_invoice.date_previous_balance = producer_invoice.producer.date_balance
        producer_invoice.date_balance = payment_date
        total_price_with_tax = producer_invoice.get_total_price_with_tax().amount
        producer_invoice.balance.amount += total_price_with_tax
        producer_invoice.save()
        Producer.objects.filter(
            id=producer_invoice.producer_id
        ).update(
            date_balance=payment_date,
            balance=F('balance') + total_price_with_tax
        )
        producer_invoice.save()

    result_set = Purchase.objects.filter(
        permanence_id=permanence.id,
        is_box_content=False,
        offer_item__price_list_multiplier__gte=DECIMAL_ONE,
        producer__represent_this_buyinggroup=False
    ).order_by('?').aggregate(
        Sum('purchase_price'),
        Sum('selling_price'),
        Sum('producer_vat'),
        Sum('customer_vat'),
    )
    if result_set["purchase_price__sum"] is not None:
        sum_purchase_price = result_set["purchase_price__sum"]
    else:
        sum_purchase_price = DECIMAL_ZERO
    if result_set["selling_price__sum"] is not None:
        sum_selling_price = result_set["selling_price__sum"]
    else:
        sum_selling_price = DECIMAL_ZERO
    if result_set["producer_vat__sum"] is not None:
        sum_producer_vat = result_set["producer_vat__sum"]
    else:
        sum_producer_vat = DECIMAL_ZERO
    if result_set["customer_vat__sum"] is not None:
        sum_customer_vat = result_set["customer_vat__sum"]
    else:
        sum_customer_vat = DECIMAL_ZERO
    purchases_delta_vat = sum_customer_vat - sum_producer_vat
    purchases_delta_price_with_tax = sum_selling_price - sum_purchase_price

    purchases_delta_price_wo_tax = purchases_delta_price_with_tax - purchases_delta_vat

    if purchases_delta_price_wo_tax != DECIMAL_ZERO:
        BankAccount.objects.create(
            permanence_id=permanence.id,
            producer=None,
            customer_id=customer_buyinggroup.id,
            operation_date=payment_date,
            operation_status=BANK_PROFIT,
            operation_comment=_("Profit") if purchases_delta_price_wo_tax >= DECIMAL_ZERO else _("Lost"),
            bank_amount_out=-purchases_delta_price_wo_tax if purchases_delta_price_wo_tax < DECIMAL_ZERO else DECIMAL_ZERO,
            bank_amount_in=purchases_delta_price_wo_tax if purchases_delta_price_wo_tax > DECIMAL_ZERO else DECIMAL_ZERO,
            customer_invoice_id=None,
            producer_invoice=None
        )
    if purchases_delta_vat != DECIMAL_ZERO:
        BankAccount.objects.create(
            permanence_id=permanence.id,
            producer=None,
            customer_id=customer_buyinggroup.id,
            operation_date=payment_date,
            operation_status=BANK_TAX,
            operation_comment=_("VAT to pay to the tax authorities") if purchases_delta_vat >= DECIMAL_ZERO else
            _("VAT to receive from the tax authorities"),
            bank_amount_out=-purchases_delta_vat if purchases_delta_vat < DECIMAL_ZERO else DECIMAL_ZERO,
            bank_amount_in=purchases_delta_vat if purchases_delta_vat > DECIMAL_ZERO else DECIMAL_ZERO,
            customer_invoice_id=None,
            producer_invoice=None
        )

    for customer_invoice in CustomerInvoice.objects.filter(
        permanence_id=permanence.id,
    ).exclude(
        customer_id=customer_buyinggroup.id,
        delta_transport=DECIMAL_ZERO
    ):
        if customer_invoice.delta_transport != DECIMAL_ZERO:
            # --> This bank movement is not a real entry
            # customer_invoice_id=customer_invoice_buyinggroup.id
            # making this, it will not be counted into the customer_buyinggroup movements twice
            # because Repanier will see it has already been counted into the customer_buyinggroup movements
            BankAccount.objects.create(
                permanence_id=permanence.id,
                producer=None,
                customer_id=customer_buyinggroup.id,
                operation_date=payment_date,
Patrick's avatar
Patrick committed
341
                operation_status=BANK_PROFIT,
Patrick's avatar
Patrick committed
342 343 344 345 346 347 348 349 350
                operation_comment="%s : %s" % (_("Transport"), customer_invoice.customer.short_basket_name),
                bank_amount_in=customer_invoice.delta_transport,
                bank_amount_out=DECIMAL_ZERO,
                customer_invoice_id=customer_invoice_buyinggroup.id,
                producer_invoice=None
            )

    # generate bank account movements
    task_producer.admin_generate_bank_account_movement(
Patrick's avatar
Patrick committed
351
        permanence=permanence, payment_date=payment_date,
Patrick's avatar
Patrick committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
        customer_buyinggroup=customer_buyinggroup
    )

    new_bank_latest_total = old_bank_latest_total

    # Calculate new current balance : Bank
    for bank_account in BankAccount.objects.select_for_update().filter(

        customer_invoice__isnull=True,
        producer_invoice__isnull=True,
        operation_status__in=[BANK_PROFIT, BANK_TAX],
        customer_id=customer_buyinggroup.id,
        operation_date__lte=payment_date
    ).order_by('?'):

        # --> This bank movement is not a real entry
        # It will not be counted into the customer_buyinggroup bank movements twice
        Customer.objects.filter(
            id=bank_account.customer_id
        ).update(
            date_balance=payment_date,
            balance=F('balance') + bank_account.bank_amount_in.amount - bank_account.bank_amount_out.amount
        )
        CustomerInvoice.objects.filter(
            customer_id=bank_account.customer_id,
            permanence_id=permanence.id,
        ).update(
            date_balance=payment_date,
            balance=F('balance') + bank_account.bank_amount_in.amount - bank_account.bank_amount_out.amount
        )
        bank_account.customer_invoice_id = customer_invoice_buyinggroup.id
        bank_account.save(update_fields=['customer_invoice'])

    for bank_account in BankAccount.objects.select_for_update().filter(
            customer_invoice__isnull=True,
            producer_invoice__isnull=True,
            customer__isnull=False,
            operation_date__lte=payment_date).order_by('?'):

        customer_invoice = CustomerInvoice.objects.filter(
            customer_id=bank_account.customer_id,
            permanence_id=permanence.id,
        ).order_by('?').first()
        if customer_invoice is None:
            customer_invoice = CustomerInvoice.objects.create(
                customer_id=bank_account.customer_id,
                permanence_id=permanence.id,
                date_previous_balance=bank_account.customer.date_balance,
                previous_balance=bank_account.customer.balance,
                date_balance=payment_date,
                balance=bank_account.customer.balance,
Patrick's avatar
Patrick committed
403
                customer_charged_id=bank_account.customer_id,
Patrick's avatar
Patrick committed
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
                transport=repanier.apps.REPANIER_SETTINGS_TRANSPORT,
                min_transport=repanier.apps.REPANIER_SETTINGS_MIN_TRANSPORT
            )
        bank_amount_in = bank_account.bank_amount_in.amount
        new_bank_latest_total += bank_amount_in
        bank_amount_out = bank_account.bank_amount_out.amount
        new_bank_latest_total -= bank_amount_out
        customer_invoice.date_balance = payment_date
        customer_invoice.bank_amount_in.amount += bank_amount_in
        customer_invoice.bank_amount_out.amount += bank_amount_out
        customer_invoice.balance.amount += (bank_amount_in - bank_amount_out)

        customer_invoice.save()
        Customer.objects.filter(
            id=bank_account.customer_id
        ).update(
            date_balance=payment_date,
            balance=F('balance') + bank_amount_in - bank_amount_out
        )
        bank_account.customer_invoice_id = customer_invoice.id
        bank_account.permanence_id = permanence.id
        bank_account.save()

    for bank_account in BankAccount.objects.select_for_update().filter(
            customer_invoice__isnull=True,
            producer_invoice__isnull=True,
            producer__isnull=False,
            operation_date__lte=payment_date).order_by('?'):

        producer_invoice = ProducerInvoice.objects.filter(
            producer_id=bank_account.producer_id,
            permanence_id=permanence.id,
        ).order_by('?').first()
        if producer_invoice is None:
            producer_invoice = ProducerInvoice.objects.create(
                producer=bank_account.producer,
                permanence_id=permanence.id,
                date_previous_balance=bank_account.producer.date_balance,
                previous_balance=bank_account.producer.balance,
                date_balance=payment_date,
Patrick's avatar
Patrick committed
444
                balance=bank_account.producer.balance
Patrick's avatar
Patrick committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
            )
        bank_amount_in = bank_account.bank_amount_in.amount
        new_bank_latest_total += bank_amount_in
        bank_amount_out = bank_account.bank_amount_out.amount
        new_bank_latest_total -= bank_amount_out
        producer_invoice.date_balance = payment_date
        producer_invoice.bank_amount_in.amount += bank_amount_in
        producer_invoice.bank_amount_out.amount += bank_amount_out
        producer_invoice.balance.amount += (bank_amount_in - bank_amount_out)
        producer_invoice.save()
        Producer.objects.filter(
            id=bank_account.producer_id
        ).update(
            date_balance=payment_date,
            balance=F('balance') + bank_amount_in - bank_amount_out
        )
        bank_account.permanence_id = permanence.id
        bank_account.producer_invoice_id = producer_invoice.id
        bank_account.save()

    BankAccount.objects.filter(
        operation_status=BANK_LATEST_TOTAL
    ).order_by('?').update(
        operation_status=BANK_NOT_LATEST_TOTAL
    )
    # Important : Create a new bank total for this permanence even if there is no bank movement
    bank_account = BankAccount.objects.create(
        permanence_id=permanence.id,
        producer=None,
        customer=None,
        operation_date=payment_date,
        operation_status=BANK_LATEST_TOTAL,
        operation_comment=cap(permanence, 100),
        bank_amount_in=new_bank_latest_total if new_bank_latest_total >= DECIMAL_ZERO else DECIMAL_ZERO,
        bank_amount_out=-new_bank_latest_total if new_bank_latest_total < DECIMAL_ZERO else DECIMAL_ZERO,
        customer_invoice=None,
        producer_invoice=None
    )

Patrick's avatar
Patrick committed
484 485
    new_status = PERMANENCE_INVOICED if repanier.apps.REPANIER_SETTINGS_INVOICE else PERMANENCE_ARCHIVED
    permanence.set_status(new_status, update_payment_date=True, payment_date=payment_date)
Patrick's avatar
Patrick committed
486

Patrick's avatar
Patrick committed
487 488 489 490 491 492 493 494 495
    ProducerInvoice.objects.filter(
        permanence_id=permanence.id
    ).update(invoice_sort_order=bank_account.id)
    CustomerInvoice.objects.filter(
        permanence_id=permanence.id
    ).update(invoice_sort_order=bank_account.id)
    Permanence.objects.filter(
        id=permanence.id
    ).update(invoice_sort_order=bank_account.id)
496 497 498


@transaction.atomic
Patrick's avatar
Patrick committed
499 500 501 502 503 504 505 506 507 508
def generate_archive(permanence):
    permanence.set_status(PERMANENCE_ARCHIVED)


@transaction.atomic
def cancel_delivery(permanence):
    permanence.set_status(PERMANENCE_CANCELLED)

@transaction.atomic
def cancel_invoice(permanence):
Patrick's avatar
Patrick committed
509
    if permanence.status in [PERMANENCE_INVOICED, PERMANENCE_ARCHIVED]:
Patrick's avatar
Patrick committed
510 511
        last_bank_account_total = BankAccount.objects.filter(
            operation_status=BANK_LATEST_TOTAL, permanence_id=permanence.id
Patrick's avatar
Patrick committed
512
        ).order_by('?').first()
Patrick's avatar
Patrick committed
513 514
        if last_bank_account_total is not None:
            # This is the last permanence invoiced
Patrick's avatar
Patrick committed
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
            getcontext().rounding = ROUND_HALF_UP
            # Historical : bo compatibility
            # permanence_id is not NULL and t.customer_id is NULL and t.producer_invoice_id is NULL
            BankAccount.objects.filter(
                operation_status='100',
                permanence__isnull=False,
                customer__isnull=True,
                producer_invoice__isnull=True,
                producer__isnull=False
            ).delete()
            # Historical : eo compatibility
            CustomerInvoice.objects.filter(
                permanence_id=permanence.id,
            ).update(
                bank_amount_in=DECIMAL_ZERO,
                bank_amount_out=DECIMAL_ZERO,
                balance=F('previous_balance'),
                date_balance=F('date_previous_balance'),
                invoice_sort_order=None
            )

Patrick's avatar
Patrick committed
536
            for customer_invoice in CustomerInvoice.objects.filter(
Patrick's avatar
Patrick committed
537
                    permanence_id=permanence.id).order_by():
538 539 540 541 542 543 544 545 546
                # customer = customer_invoice.customer
                # customer.balance = customer_invoice.previous_balance
                # customer.date_balance = customer_invoice.date_previous_balance
                # customer.save(update_fields=['balance', 'date_balance'])
                # use vvvv because ^^^^^ will call "pre_save" function which reset valid_email to None
                models.Customer.objects.filter(id=customer_invoice.customer_id).order_by('?').update(
                    balance=customer_invoice.previous_balance,
                    date_balance=customer_invoice.date_previous_balance
                )
Patrick's avatar
Patrick committed
547 548 549 550 551
                BankAccount.objects.all().filter(
                    customer_invoice_id=customer_invoice.id
                ).update(
                    customer_invoice=None
                )
Patrick's avatar
Patrick committed
552
            ProducerInvoice.objects.filter(
Patrick's avatar
Patrick committed
553
                permanence_id=permanence.id
Patrick's avatar
Patrick committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
            ).exclude(
                producer__represent_this_buyinggroup=True
            ).update(
                bank_amount_in=DECIMAL_ZERO,
                bank_amount_out=DECIMAL_ZERO,
                delta_price_with_tax=DECIMAL_ZERO,
                delta_vat=DECIMAL_ZERO,
                delta_transport=DECIMAL_ZERO,
                delta_deposit=DECIMAL_ZERO,
                delta_stock_with_tax=DECIMAL_ZERO,
                delta_stock_vat=DECIMAL_ZERO,
                delta_stock_deposit=DECIMAL_ZERO,
                balance=F('previous_balance'),
                date_balance=F('date_previous_balance'),
                invoice_sort_order=None
            )
            # Important : Restore delta from delivery points added into invoice.confirm_order()
            ProducerInvoice.objects.filter(
                permanence_id=permanence.id,
                producer__represent_this_buyinggroup=True
Patrick's avatar
Patrick committed
574
            ).update(
Patrick's avatar
Patrick committed
575 576 577 578 579 580 581
                bank_amount_in=DECIMAL_ZERO,
                bank_amount_out=DECIMAL_ZERO,
                delta_stock_with_tax=DECIMAL_ZERO,
                delta_stock_vat=DECIMAL_ZERO,
                delta_stock_deposit=DECIMAL_ZERO,
                balance=F('previous_balance'),
                date_balance=F('date_previous_balance'),
Patrick's avatar
Patrick committed
582 583
                invoice_sort_order=None
            )
Patrick's avatar
Patrick committed
584 585


Patrick's avatar
Patrick committed
586
            for producer_invoice in ProducerInvoice.objects.filter(
Patrick's avatar
Patrick committed
587 588
                    permanence_id=permanence.id
            ).order_by('?'): # .distinct("id"):
Patrick's avatar
Patrick committed
589 590 591 592 593 594 595 596 597
                producer = producer_invoice.producer
                producer.balance = producer_invoice.previous_balance
                producer.date_balance = producer_invoice.date_previous_balance
                producer.save(update_fields=['balance', 'date_balance'])
                BankAccount.objects.all().filter(
                    producer_invoice_id=producer_invoice.id
                ).update(
                    producer_invoice=None
                )
Patrick's avatar
Patrick committed
598
            # IMPORTANT : Do not update stock when canceling
Patrick's avatar
Patrick committed
599 600 601 602 603 604 605
            last_bank_account_total.delete()
            bank_account = BankAccount.objects.filter(
                customer=None,
                producer=None).order_by('-id').first()
            if bank_account is not None:
                bank_account.operation_status = BANK_LATEST_TOTAL
                bank_account.save()
Patrick's avatar
Patrick committed
606
            # Delete also all payments recorded to producers, bank profit, bank tax
Patrick's avatar
Patrick committed
607 608 609
            # Delete also all compensation recorded to producers
            BankAccount.objects.filter(
                permanence_id=permanence.id,
Patrick's avatar
Patrick committed
610 611 612 613
                operation_status__in=[
                    BANK_CALCULATED_INVOICE,
                    BANK_PROFIT,
                    BANK_TAX,
Patrick's avatar
Patrick committed
614
                    BANK_MEMBERSHIP_FEE,
Patrick's avatar
Patrick committed
615 616 617
                    BANK_COMPENSATION # BANK_COMPENSATION may occurs in previous release of Repanier
                ]
            ).order_by('?').delete()
Patrick's avatar
Patrick committed
618
        Permanence.objects.filter(
Patrick's avatar
Patrick committed
619
            id=permanence.id
Patrick's avatar
Patrick committed
620
        ).update(invoice_sort_order=None)
Patrick's avatar
Patrick committed
621 622
        permanence.set_status(PERMANENCE_SEND)

623

Patrick's avatar
Patrick committed
624 625 626 627 628 629 630
@transaction.atomic
def cancel_archive(permanence):
    if BankAccount.objects.filter(
        operation_status=BANK_LATEST_TOTAL, permanence_id=permanence.id
    ).order_by('?').exists():
        # old archive
        cancel_invoice(permanence)
Patrick's avatar
Patrick committed
631
    else:
Patrick's avatar
Patrick committed
632
        permanence.set_status(PERMANENCE_SEND, allow_downgrade=True)
633 634


Patrick's avatar
Patrick committed
635 636
def admin_cancel(permanence):
    if permanence.status == PERMANENCE_INVOICED:
Patrick's avatar
Patrick committed
637 638 639 640 641 642 643 644
        latest_total = BankAccount.objects.filter(
            operation_status=BANK_LATEST_TOTAL).only(
            "permanence"
        ).first()
        if latest_total is not None:
            last_permanence_invoiced_id = latest_total.permanence_id
            if last_permanence_invoiced_id is not None:
                if last_permanence_invoiced_id == permanence.id:
645
                    # This is well the latest closed permanence. The invoices can be cancelled without damages.
Patrick's avatar
Patrick committed
646
                    cancel_invoice(permanence)
647 648
                    user_message = _("The selected invoice has been canceled.")
                    user_message_level = messages.INFO
Patrick's avatar
Patrick committed
649 650 651
                else:
                    user_message = _("The selected invoice is not the latest invoice.")
                    user_message_level = messages.ERROR
Patrick's avatar
Patrick committed
652 653 654 655 656 657
            else:
                user_message = _("The selected invoice is not the latest invoice.")
                user_message_level = messages.ERROR
        else:
            user_message = _("The selected invoice has been canceled.")
            user_message_level = messages.INFO
Patrick's avatar
Patrick committed
658
            permanence.set_status(PERMANENCE_SEND)
Patrick's avatar
Patrick committed
659 660
    elif permanence.status in [PERMANENCE_ARCHIVED, PERMANENCE_CANCELLED]:
            cancel_archive(permanence)
Patrick's avatar
Patrick committed
661 662
            user_message = _("The selected invoice has been restored.")
            user_message_level = messages.INFO
Patrick's avatar
Patrick committed
663 664 665 666 667
    else:
        user_message = _("The status of %(permanence)s prohibit you to cancel invoices.") % {
            'permanence': permanence}
        user_message_level = messages.ERROR

668
    return user_message, user_message_level
Patrick's avatar
Patrick committed
669 670 671 672


def admin_send(permanence):
    if permanence.status == PERMANENCE_INVOICED:
Patrick's avatar
Patrick committed
673 674 675
        # thread.start_new_thread(email_invoice.send_invoice, (permanence.id,))
        t = threading.Thread(target=email_invoice.send_invoice, args=(permanence.id,))
        t.start()
Patrick's avatar
Patrick committed
676 677 678 679 680 681 682 683
        user_message = _("Emails containing the invoices will be send to the customers and the producers.")
        user_message_level = messages.INFO
    else:
        user_message = _("The status of %(permanence)s prohibit you to send invoices.") % {
            'permanence': permanence}
        user_message_level = messages.ERROR

    return user_message, user_message_level