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

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

import repanier.apps
from repanier.models import DeliveryBoard
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 20 21 22 23 24
from repanier.models import Purchase
from repanier.task import task_producer
from repanier.tools import *


@transaction.atomic
Patrick's avatar
Patrick committed
25 26
def generate_invoice(permanence, payment_date):
    initial_permanence = permanence
Patrick's avatar
Patrick committed
27 28 29 30 31 32 33
    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
    getcontext().rounding = ROUND_HALF_UP
    old_bank_latest_total = bank_account.bank_amount_in.amount - bank_account.bank_amount_out.amount
Patrick's avatar
Patrick committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
    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:

        # The values_list method returns a ValuesListQuerySet.
        # This means it has the advantages of a queryset. For example it is lazy, so you only fetch the first 25 elements from the database when you slice it.
        # To convert it to a list for e.g. because you reuse it, use list()
        producers_to_invoice = list(ProducerInvoice.objects.filter(
            permanence_id=permanence.id,
            invoice_sort_order__isnull=True,
            to_be_paid=True
        ).values_list('producer_id', flat=True).order_by("producer_id"))
Patrick's avatar
Patrick committed
49

Patrick's avatar
Patrick committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
        new_permanence = permanence.create_child()
        # It's not needed to set new_permanence.status = PERMANENCE_WAIT_FOR_INVOICED because we are in @transaction

        for purchase in Purchase.objects.filter(
            permanence_id=permanence.id,
            producer_id__in=producers_to_invoice
        ).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)
            new_customer_invoice.set_delivery(customer_invoice.delivery)
            Purchase.objects.filter(
                customer_invoice_id=customer_invoice.id,
                producer_id__in=producers_to_invoice
            ).order_by('?').update(
                permanence_id=new_permanence.id,
                customer_invoice_id=new_customer_invoice.id,
                customer_charged_id=new_customer_invoice.customer_charged_id
            )
            new_customer_invoice.calculate_and_save_delta_buyinggroup()
            customer_invoice.calculate_and_save_delta_buyinggroup()

        ProducerInvoice.objects.filter(
            permanence_id=permanence.id,
            producer_id__in=producers_to_invoice
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )
        CustomerProducerInvoice.objects.filter(
            permanence_id=permanence.id,
            producer_id__in=producers_to_invoice
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )
        OfferItem.objects.filter(
            permanence_id=permanence.id,
            producer_id__in=producers_to_invoice
        ).order_by('?').update(
            permanence_id=new_permanence.id
        )
        permanence = new_permanence

    else:
        for customer_invoice in CustomerInvoice.objects.filter(
                permanence_id=permanence.id).order_by('?'):
            # In case of changed delivery conditions
            customer_invoice.set_delivery(customer_invoice.delivery)
            # Need to calculate delta_price_with_tax, delta_vat and delta_transport
            customer_invoice.calculate_and_save_delta_buyinggroup()
Patrick's avatar
Patrick committed
100 101 102
            Purchase.objects.filter(
                customer_invoice_id=customer_invoice.id
            ).order_by('?').update(
Patrick's avatar
Patrick committed
103
                customer_charged_id=customer_invoice.customer_charged_id
Patrick's avatar
Patrick committed
104 105 106 107 108 109 110 111 112 113
            )


    # Important : linked to task_invoice.cancel
    for delivery in DeliveryBoard.objects.filter(
            permanence_id=permanence.id,
            delivery_point__customer_responsible__isnull=False
    ):
        result_set = CustomerInvoice.objects.filter(
            permanence_id=permanence.id,
Patrick's avatar
Patrick committed
114
            customer_charged_id=delivery.delivery_point.customer_responsible_id
Patrick's avatar
Patrick committed
115 116 117 118 119 120 121 122 123 124
        ).exclude(
            customer_id=delivery.delivery_point.customer_responsible_id
        ).order_by('?').aggregate(
            Sum('total_price_with_tax'),
            Sum('total_vat'),
            Sum('delta_price_with_tax'),
            Sum('delta_vat'),
        )
        if result_set["total_price_with_tax__sum"] is not None:
            sum_total_price_with_tax = result_set["total_price_with_tax__sum"]
Patrick's avatar
Patrick committed
125
        else:
Patrick's avatar
Patrick committed
126 127 128 129 130 131 132 133 134 135 136 137 138
            sum_total_price_with_tax = DECIMAL_ZERO
        if result_set["total_vat__sum"] is not None:
            sum_total_vat = result_set["total_vat__sum"]
        else:
            sum_total_vat = DECIMAL_ZERO
        if result_set["delta_price_with_tax__sum"] is not None:
            sum_delta_price_with_tax = result_set["delta_price_with_tax__sum"]
        else:
            sum_delta_price_with_tax = DECIMAL_ZERO
        if result_set["delta_vat__sum"] is not None:
            sum_delta_vat = result_set["delta_vat__sum"]
        else:
            sum_delta_vat = DECIMAL_ZERO
Patrick's avatar
Patrick committed
139

Patrick's avatar
Patrick committed
140 141 142 143 144 145 146 147 148 149 150
        CustomerInvoice.objects.filter(
            permanence_id=permanence.id,
            customer_id=delivery.delivery_point.customer_responsible_id
        ).update(
            is_order_confirm_send=True,
            total_price_with_tax=sum_total_price_with_tax,
            total_vat=sum_total_vat,
            delta_price_with_tax=sum_delta_price_with_tax,
            delta_vat=sum_delta_vat,
            delta_transport=DECIMAL_ZERO,
        )
Patrick's avatar
Patrick committed
151
        customer_invoice_charged = CustomerInvoice.objects.filter(
Patrick's avatar
Patrick committed
152 153 154
            permanence_id=permanence.id,
            customer_id=delivery.delivery_point.customer_responsible_id
        ).order_by('?').first()
Patrick's avatar
Patrick committed
155
        customer_invoice_charged.calculate_and_save_delta_buyinggroup()
156

Patrick's avatar
Patrick committed
157 158 159 160 161 162 163 164 165 166 167 168
    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,
Patrick's avatar
Patrick committed
169
            customer_charged_id=customer_buyinggroup.id,
Patrick's avatar
Patrick committed
170 171 172 173
            transport=repanier.apps.REPANIER_SETTINGS_TRANSPORT,
            min_transport=repanier.apps.REPANIER_SETTINGS_MIN_TRANSPORT,
            price_list_multiplier=DECIMAL_ONE
        )
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
            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
        )

Patrick's avatar
Patrick committed
325
    for membership_fee in Purchase.objects.filter(
Patrick's avatar
Patrick committed
326 327
        permanence_id=permanence.id,
        producer_id=producer_buyinggroup.id,
Patrick's avatar
Patrick committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
        offer_item__order_unit=PRODUCT_ORDER_UNIT_MEMBERSHIP_FEE
    ).order_by('?').aggregate(Sum('selling_price')):
        # --> This bank movement is not a real entry
        # 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,
            operation_status=BANK_MEMBERSHIP_FEE,
            operation_comment="%s : %s" % (_("Membership fee"), membership_fee.customer),
            bank_amount_in=membership_fee,
            bank_amount_out=DECIMAL_ZERO,
            customer_invoice_id=customer_invoice_buyinggroup.id,
            producer_invoice=None
        )
Patrick's avatar
Patrick committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

    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
362
                operation_status=BANK_PROFIT,
Patrick's avatar
Patrick committed
363 364 365 366 367 368 369 370 371
                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
372
        permanence=permanence, payment_date=payment_date,
Patrick's avatar
Patrick committed
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 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
        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
424
                customer_charged_id=bank_account.customer_id,
Patrick's avatar
Patrick committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
                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
465
                balance=bank_account.producer.balance
Patrick's avatar
Patrick committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
            )
        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
505 506 507 508 509
    if permanence_partially_invoiced:
        initial_permanence.set_status(PERMANENCE_SEND)

    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
510

Patrick's avatar
Patrick committed
511 512 513 514 515 516 517 518 519
    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)
520 521 522


@transaction.atomic
Patrick's avatar
Patrick committed
523 524 525 526 527 528 529 530 531 532
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
533
    if permanence.status in [PERMANENCE_INVOICED, PERMANENCE_ARCHIVED]:
Patrick's avatar
Patrick committed
534 535
        last_bank_account_total = BankAccount.objects.filter(
            operation_status=BANK_LATEST_TOTAL, permanence_id=permanence.id
Patrick's avatar
Patrick committed
536
        ).order_by('?').first()
Patrick's avatar
Patrick committed
537 538
        if last_bank_account_total is not None:
            # This is the last permanence invoiced
Patrick's avatar
Patrick committed
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
            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
            )
            # Important : linked to task_invoice.generate
            for delivery in DeliveryBoard.objects.filter(
                    permanence_id=permanence.id,
                    delivery_point__customer_responsible__isnull=False
            ):
                CustomerInvoice.objects.filter(
                    permanence_id=permanence.id,
                    customer_id=delivery.delivery_point.customer_responsible_id
                ).update(
                    total_price_with_tax=DECIMAL_ZERO,
                    total_vat=DECIMAL_ZERO,
                    delta_price_with_tax=DECIMAL_ZERO,
                    delta_vat=DECIMAL_ZERO,
                    delta_transport=DECIMAL_ZERO
                )

Patrick's avatar
Patrick committed
575
            for customer_invoice in CustomerInvoice.objects.filter(
Patrick's avatar
Patrick committed
576
                    permanence_id=permanence.id).order_by():
Patrick's avatar
Patrick committed
577 578 579 580 581 582 583 584 585
                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'])
                BankAccount.objects.all().filter(
                    customer_invoice_id=customer_invoice.id
                ).update(
                    customer_invoice=None
                )
Patrick's avatar
Patrick committed
586
            ProducerInvoice.objects.filter(
Patrick's avatar
Patrick committed
587
                permanence_id=permanence.id
Patrick's avatar
Patrick committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
            ).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
608
            ).update(
Patrick's avatar
Patrick committed
609 610 611 612 613 614 615
                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
616 617
                invoice_sort_order=None
            )
Patrick's avatar
Patrick committed
618 619


Patrick's avatar
Patrick committed
620
            for producer_invoice in ProducerInvoice.objects.filter(
Patrick's avatar
Patrick committed
621 622
                    permanence_id=permanence.id
            ).order_by('?'): # .distinct("id"):
Patrick's avatar
Patrick committed
623 624 625 626 627 628 629 630 631
                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
632
            # IMPORTANT : Do not update stock when canceling
Patrick's avatar
Patrick committed
633 634 635 636 637 638 639
            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
640
            # Delete also all payments recorded to producers, bank profit, bank tax
Patrick's avatar
Patrick committed
641 642 643
            # Delete also all compensation recorded to producers
            BankAccount.objects.filter(
                permanence_id=permanence.id,
Patrick's avatar
Patrick committed
644 645 646 647
                operation_status__in=[
                    BANK_CALCULATED_INVOICE,
                    BANK_PROFIT,
                    BANK_TAX,
Patrick's avatar
Patrick committed
648
                    BANK_MEMBERSHIP_FEE,
Patrick's avatar
Patrick committed
649 650 651
                    BANK_COMPENSATION # BANK_COMPENSATION may occurs in previous release of Repanier
                ]
            ).order_by('?').delete()
Patrick's avatar
Patrick committed
652 653 654
        Permanence.objects.filter(
            permanence_id=permanence.id
        ).update(invoice_sort_order=None)
Patrick's avatar
Patrick committed
655 656
        permanence.set_status(PERMANENCE_SEND)

657

Patrick's avatar
Patrick committed
658 659 660 661 662 663 664
@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
665
    else:
Patrick's avatar
Patrick committed
666
        permanence.set_status(PERMANENCE_SEND, allow_downgrade=True)
667 668


Patrick's avatar
Patrick committed
669 670
def admin_cancel(permanence):
    if permanence.status == PERMANENCE_INVOICED:
Patrick's avatar
Patrick committed
671 672 673 674 675 676 677 678
        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:
679
                    # This is well the latest closed permanence. The invoices can be cancelled without damages.
Patrick's avatar
Patrick committed
680
                    cancel_invoice(permanence)
681 682
                    user_message = _("The selected invoice has been canceled.")
                    user_message_level = messages.INFO
Patrick's avatar
Patrick committed
683 684 685
                else:
                    user_message = _("The selected invoice is not the latest invoice.")
                    user_message_level = messages.ERROR
Patrick's avatar
Patrick committed
686 687 688 689 690 691
            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
692
            permanence.set_status(PERMANENCE_SEND)
Patrick's avatar
Patrick committed
693 694
    elif permanence.status in [PERMANENCE_ARCHIVED, PERMANENCE_CANCELLED]:
            cancel_archive(permanence)
Patrick's avatar
Patrick committed
695 696
            user_message = _("The selected invoice has been restored.")
            user_message_level = messages.INFO
Patrick's avatar
Patrick committed
697 698 699 700 701
    else:
        user_message = _("The status of %(permanence)s prohibit you to cancel invoices.") % {
            'permanence': permanence}
        user_message_level = messages.ERROR

702
    return user_message, user_message_level
Patrick's avatar
Patrick committed
703 704 705 706 707 708 709 710 711 712 713 714 715


def admin_send(permanence):
    if permanence.status == PERMANENCE_INVOICED:
        thread.start_new_thread(email_invoice.send_invoice, (permanence.id,))
        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