def user_expense_perm(user): """compute user perm and returns expense_administrator, expense_subsidiary_manager, expense_manager, expense_paymaster, expense_requester""" expense_administrator = user.is_superuser or user_has_feature( user, "expense_administrator") expense_subsidiary_manager = expense_administrator or user_has_feature( user, "expense_subsidiary_manager") expense_manager = expense_administrator or user_has_feature( user, "expense_manager") expense_paymaster = expense_administrator or user_has_feature( user, "expense_paymaster") expense_requester = expense_administrator or user_has_feature( user, "expense") return expense_administrator, expense_subsidiary_manager, expense_manager, expense_paymaster, expense_requester
def __getitem__(self, feature): return user_has_feature(self.user, str(feature))
def bill_review(request): """Review of bills: bills overdue, due soon, or to be created""" today = date.today() wait_warning = timedelta( 15) # wait in days used to warn that a bill is due soon subsidiary = get_subsidiary_from_session(request) # Get bills overdue, due soon, litigious and recently paid overdue_bills = ClientBill.objects.filter( state="1_SENT", due_date__lte=today).select_related() soondue_bills = ClientBill.objects.filter( state="1_SENT", due_date__gt=today, due_date__lte=(today + wait_warning)).select_related() recent_bills = ClientBill.objects.filter( state="2_PAID").order_by("-payment_date").select_related() litigious_bills = ClientBill.objects.filter( state="3_LITIGIOUS").select_related() supplier_overdue_bills = SupplierBill.objects.filter( state__in=("1_RECEIVED", "1_VALIDATED"), due_date__lte=today).select_related() supplier_soondue_bills = SupplierBill.objects.filter( state__in=("1_RECEIVED", "1_VALIDATED"), due_date__gt=today).select_related() # Filter bills on subsidiary if defined if subsidiary: overdue_bills = overdue_bills.filter(lead__subsidiary=subsidiary) soondue_bills = soondue_bills.filter(lead__subsidiary=subsidiary) recent_bills = recent_bills.filter(lead__subsidiary=subsidiary) litigious_bills = litigious_bills.filter(lead__subsidiary=subsidiary) supplier_overdue_bills = supplier_overdue_bills.filter( lead__subsidiary=subsidiary) supplier_soondue_bills = supplier_soondue_bills.filter( lead__subsidiary=subsidiary) # Limit recent bill to last 20 ones recent_bills = recent_bills[:20] # Compute totals soondue_bills_total = soondue_bills.aggregate(Sum("amount"))["amount__sum"] overdue_bills_total = overdue_bills.aggregate(Sum("amount"))["amount__sum"] litigious_bills_total = litigious_bills.aggregate( Sum("amount"))["amount__sum"] soondue_bills_total_with_vat = sum([ bill.amount_with_vat for bill in soondue_bills if bill.amount_with_vat ]) overdue_bills_total_with_vat = sum([ bill.amount_with_vat for bill in overdue_bills if bill.amount_with_vat ]) litigious_bills_total_with_vat = sum([ bill.amount_with_vat for bill in litigious_bills if bill.amount_with_vat ]) # Get leads with done timesheet in past three month that don't have bill yet leads_without_bill = Lead.objects.filter( state="WON", mission__timesheet__working_date__gte=(date.today() - timedelta(90))) leads_without_bill = leads_without_bill.annotate( Count("clientbill")).filter(clientbill__count=0) if subsidiary: leads_without_bill = leads_without_bill.filter(subsidiary=subsidiary) return render( request, "billing/bill_review.html", { "overdue_bills": overdue_bills, "soondue_bills": soondue_bills, "recent_bills": recent_bills, "litigious_bills": litigious_bills, "soondue_bills_total": soondue_bills_total, "overdue_bills_total": overdue_bills_total, "litigious_bills_total": litigious_bills_total, "soondue_bills_total_with_vat": soondue_bills_total_with_vat, "overdue_bills_total_with_vat": overdue_bills_total_with_vat, "litigious_bills_total_with_vat": litigious_bills_total_with_vat, "leads_without_bill": leads_without_bill, "supplier_soondue_bills": supplier_soondue_bills, "supplier_overdue_bills": supplier_overdue_bills, "billing_management": user_has_feature(request.user, "billing_management"), "consultant": Consultant.objects.filter( trigramme__iexact=request.user.username).first(), "user": request.user })
def client_bill(request, bill_id=None): """Add or edit client bill""" billDetailFormSet = None billExpenseFormSet = None billing_management_feature = "billing_management" forbiden = HttpResponseRedirect(reverse("core:forbiden")) if bill_id: try: bill = ClientBill.objects.get(id=bill_id) except ClientBill.DoesNotExist: raise Http404 else: bill = None BillDetailFormSet = inlineformset_factory(ClientBill, BillDetail, formset=BillDetailInlineFormset, form=BillDetailForm, fields="__all__") BillExpenseFormSet = inlineformset_factory( ClientBill, BillExpense, formset=BillExpenseInlineFormset, form=BillExpenseForm, fields="__all__") wip_status = ("0_DRAFT", "0_PROPOSED") if request.POST: form = ClientBillForm(request.POST, request.FILES, instance=bill) # First, ensure user is allowed to manipulate the bill if bill and bill.state not in wip_status and not user_has_feature( request.user, billing_management_feature): return forbiden if form.data["state"] not in wip_status and not user_has_feature( request.user, billing_management_feature): return forbiden # Now, process form if bill and bill.state in wip_status: billDetailFormSet = BillDetailFormSet(request.POST, instance=bill) billExpenseFormSet = BillExpenseFormSet(request.POST, instance=bill) if form.is_valid() and (billDetailFormSet is None or billDetailFormSet.is_valid()) and ( billExpenseFormSet is None or billExpenseFormSet.is_valid()): bill = form.save() if billDetailFormSet: billDetailFormSet.save() if billExpenseFormSet: billExpenseFormSet.save() bill.save() # Again, to take into account modified details. if bill.state in wip_status: success_url = reverse_lazy("billing:client_bill", args=[ bill.id, ]) else: success_url = request.GET.get( 'return_to', False) or reverse_lazy( "crm:company_detail", args=[ bill.lead.client.organisation.company.id, ]) + "#goto_tab-billing" if bill.bill_file: if form.changed_data == [ "state" ] and billDetailFormSet is None and billExpenseFormSet is None: # only state has change. No need to regenerate bill file. messages.add_message(request, messages.INFO, _("Bill state has beed updated")) elif "bill_file" in form.changed_data: # a file has been provided by user himself. We must not generate a file and overwrite it. messages.add_message( request, messages.WARNING, _("Using custom user file to replace current bill") ) else: # bill file exist but authorized admin change information and do not provide custom file. Let's generate again bill file messages.add_message( request, messages.WARNING, _("A new bill is generated and replace the previous one" )) if os.path.exists(bill.bill_file.path): os.remove(bill.bill_file.path) generate_bill_pdf(bill, request) else: # Bill file still not exist. Let's create it messages.add_message( request, messages.INFO, _("A new bill file has been generated")) generate_bill_pdf(bill, request) return HttpResponseRedirect(success_url) else: if bill: # Create a form to edit the given bill form = ClientBillForm(instance=bill) if bill.state in wip_status: billDetailFormSet = BillDetailFormSet(instance=bill) billExpenseFormSet = BillExpenseFormSet(instance=bill) else: # Still no bill, let's create it with its detail if at least mission or lead has been provided missions = [] if request.GET.get("lead"): lead = Lead.objects.get(id=request.GET.get("lead")) missions = lead.mission_set.all() # take all missions if request.GET.get("mission"): missions = [Mission.objects.get(id=request.GET.get("mission"))] if missions: bill = ClientBill.objects.create(lead=missions[0].lead) bill.save() for mission in missions: if mission.billing_mode == "TIME_SPENT": if request.GET.get("start_date") and request.GET.get( "end_date"): start_date = date( int(request.GET.get("start_date")[0:4]), int(request.GET.get("start_date")[4:6]), 1) end_date = date(int(request.GET.get("end_date")[0:4]), int(request.GET.get("end_date")[4:6]), 1) else: start_date = previousMonth(previousMonth(date.today())) end_date = previousMonth(date.today()) update_client_bill_from_timesheet(bill, mission, start_date, end_date) else: # FIXED_PRICE mission proportion = request.GET.get("proportion", 0.30) bill = update_client_bill_from_proportion( bill, mission, proportion=proportion) if bill: form = ClientBillForm(instance=bill) billDetailFormSet = BillDetailFormSet(instance=bill) billExpenseFormSet = BillExpenseFormSet(instance=bill) else: # Simple virgin new form form = ClientBillForm() return render( request, "billing/client_bill_form.html", { "bill_form": form, "detail_formset": billDetailFormSet, "detail_formset_helper": BillDetailFormSetHelper(), "expense_formset": billExpenseFormSet, "expense_formset_helper": BillExpenseFormSetHelper(), "bill_id": bill.id if bill else None, "can_delete": bill.state in wip_status if bill else False, "can_preview": bill.state in wip_status if bill else False, "user": request.user })
def client_bill(request, bill_id=None): """Add or edit client bill""" billDetailFormSet = None billExpenseFormSet = None billing_management_feature = "billing_management" forbiden = HttpResponseRedirect(reverse("core:forbiden")) if bill_id: try: bill = ClientBill.objects.get(id=bill_id) except ClientBill.DoesNotExist: raise Http404 else: bill = None BillDetailFormSet = inlineformset_factory(ClientBill, BillDetail, formset=BillDetailInlineFormset, form=BillDetailForm, fields="__all__") BillExpenseFormSet = inlineformset_factory( ClientBill, BillExpense, formset=BillExpenseInlineFormset, form=BillExpenseForm, fields="__all__") wip_status = ("0_DRAFT", "0_PROPOSED") if request.POST: form = ClientBillForm(request.POST, request.FILES, instance=bill) # First, ensure user is allowed to manipulate the bill if bill and bill.state not in wip_status and not user_has_feature( request.user, billing_management_feature): return forbiden if form.data["state"] not in wip_status and not user_has_feature( request.user, billing_management_feature): return forbiden # Now, process form if bill and bill.state in wip_status: billDetailFormSet = BillDetailFormSet(request.POST, instance=bill) billExpenseFormSet = BillExpenseFormSet(request.POST, instance=bill) if form.is_valid() and (billDetailFormSet is None or billDetailFormSet.is_valid()) and ( billExpenseFormSet is None or billExpenseFormSet.is_valid()): bill = form.save() if billDetailFormSet: billDetailFormSet.save() if billExpenseFormSet: billExpenseFormSet.save() bill.save() # Again, to take into account modified details. if bill.state in wip_status: success_url = reverse_lazy("billing:client_bill", args=[ bill.id, ]) else: success_url = request.GET.get( 'return_to', False) or reverse_lazy( "crm:company_detail", args=[ bill.lead.client.organisation.company.id, ]) + "#goto_tab-billing" if not bill.bill_file: fake_http_request = request fake_http_request.method = "GET" response = BillPdf.as_view()(fake_http_request, bill_id=bill.id) pdf = response.rendered_content.read() filename = bill_pdf_filename(bill) content = ContentFile(pdf, name=filename) bill.bill_file.save(filename, content) bill.save() return HttpResponseRedirect(success_url) else: if bill: form = ClientBillForm(instance=bill) if bill.state in wip_status: billDetailFormSet = BillDetailFormSet(instance=bill) billExpenseFormSet = BillExpenseFormSet(instance=bill) else: # Still no bill, let's create it with its detail if at least mission has been provided if request.GET.get("mission"): mission = Mission.objects.get(id=request.GET.get("mission")) if mission.billing_mode == "TIME_SPENT": if request.GET.get("month") and request.GET.get("year"): month = date(int(request.GET.get("year")), int(request.GET.get("month")), 1) else: month = date.today().replace(day=1) bill = create_client_bill_from_timesheet(mission, month) else: # FIXED_PRICE mission proportion = request.GET.get("proportion", 0.30) bill = create_client_bill_from_proportion( mission, proportion=proportion) form = ClientBillForm(instance=bill) billDetailFormSet = BillDetailFormSet(instance=bill) billExpenseFormSet = BillExpenseFormSet(instance=bill) else: form = ClientBillForm() return render( request, "billing/client_bill_form.html", { "bill_form": form, "detail_formset": billDetailFormSet, "detail_formset_helper": BillDetailFormSetHelper(), "expense_formset": billExpenseFormSet, "expense_formset_helper": BillExpenseFormSetHelper(), "bill_id": bill.id if bill else None, "can_delete": bill.state in wip_status if bill else False, "can_preview": bill.state in wip_status if bill else False, "user": request.user })