Пример #1
0
def warnForImcompleteTimesheet(warnSurbooking=False, days=None, month=None):
    """Warn users and admin for incomplete timesheet after due date
    @param warnSurbooking: Warn for surbooking days (default is false)
    @param day: only check n first days. If none, check all month"""
    emailTemplate = get_template("batch/timesheet_warning_email.txt")
    if month == "current":
        nextMonth = (date.today().replace(day=1) +
                     timedelta(days=40)).replace(day=1)
        currentMonth = date.today().replace(day=1)
    else:
        # Checking past month
        nextMonth = date.today().replace(day=1)
        currentMonth = (nextMonth - timedelta(days=5)).replace(day=1)

    mails = []  # List of mail to be sent
    for consultant in Consultant.objects.filter(active=True,
                                                subcontractor=False):
        recipients = []
        if not [
                m for m in consultant.forecasted_missions(currentMonth)
                if m.nature == "PROD"
        ]:
            # No productive mission forecasted on current month
            # Consultant may have just started
            # No check needed. Skip it
            continue
        missions = consultant.timesheet_missions(month=currentMonth)
        timesheetData, timesheetTotal, warning = gatherTimesheetData(
            consultant, missions, currentMonth)
        url = pydici.settings.PYDICI_HOST + urlresolvers.reverse(
            "people.views.consultant_home", args=[consultant.id])
        url += "?year=%s;month=%s" % (currentMonth.year, currentMonth.month)
        url += "#tab-timesheet"

        # Truncate if day parameter was given
        if days:
            warning = warning[:days]
        warning = [i for i in warning if i]  # Remove None
        if sum(warning) > 0:
            surbookingDays = warning.count(1)
            incompleteDays = warning.count(2)
            if not warnSurbooking and not incompleteDays:
                continue  # Don't cry if user only have surbooking issue

            user = consultant.getUser()
            if user and user.email:
                recipients.append(user.email)
            if consultant.manager:
                managerUser = consultant.manager.getUser()
                if managerUser and managerUser.email:
                    recipients.append(managerUser.email)

            if recipients:
                msgText = emailTemplate.render(
                    Context({
                        "month": currentMonth,
                        "surbooking_days": surbookingDays,
                        "incomplete_days": incompleteDays,
                        "consultant": consultant,
                        "days": days,
                        "url": url
                    }))
                mails.append(
                    ((_("[pydici] Your timesheet is not correct"), msgText,
                      pydici.settings.LEADS_MAIL_FROM, recipients)))
            else:
                mails.append(((
                    _("[pydici] User has no email"),
                    _("User %s has an incomplete timesheet but cannot be warned because he has no email."
                      % consultant), pydici.settings.LEADS_MAIL_FROM, [
                          pydici.settings.LEADS_MAIL_FROM,
                      ])))

    # Send all emails in one time
    send_mass_mail(mails, fail_silently=False)
Пример #2
0
def pre_billing(request, year=None, month=None, mine=False):
    """Pre billing page: help to identify bills to send"""
    if year and month:
        month = date(int(year), int(month), 1)
    else:
        month = previousMonth(date.today())

    next_month = nextMonth(month)
    timeSpentBilling = {}  # Key is lead, value is total and dict of mission(total, Mission billingData)
    rates = {}  # Key is mission, value is Consultant rates dict

    try:
        billing_consultant = Consultant.objects.get(trigramme__iexact=request.user.username)
    except Consultant.DoesNotExist:
        billing_consultant = None
        mine = False

    # Check consultant timesheet to hint if billing could be done based on a clean state
    timesheet_ok = {}
    for consultant in Consultant.objects.filter(active=True, subcontractor=False):
        missions = consultant.timesheet_missions(month=month)
        timesheetData, timesheetTotal, warning = gatherTimesheetData(consultant, missions, month)
        days = sum([v for (k,v) in timesheetTotal.items() if k!="ticket"])  # Compute timesheet days. Remove lunch ticket count
        if days == working_days(month, holidayDays(month=month)):
            timesheet_ok[consultant.id] = True
        else:
            timesheet_ok[consultant.id] = False

    fixedPriceMissions = Mission.objects.filter(nature="PROD", billing_mode="FIXED_PRICE",
                                                timesheet__working_date__gte=month,
                                                timesheet__working_date__lt=next_month)
    undefinedBillingModeMissions = Mission.objects.filter(nature="PROD", billing_mode=None,
                                                          timesheet__working_date__gte=month,
                                                          timesheet__working_date__lt=next_month)
    if mine:
        fixedPriceMissions = fixedPriceMissions.filter(Q(lead__responsible=billing_consultant) | Q(responsible=billing_consultant))
        undefinedBillingModeMissions = undefinedBillingModeMissions.filter(Q(lead__responsible=billing_consultant) | Q(responsible=billing_consultant))

    fixedPriceMissions = fixedPriceMissions.order_by("lead").distinct()
    undefinedBillingModeMissions = undefinedBillingModeMissions.order_by("lead").distinct()

    timesheets = Timesheet.objects.filter(working_date__gte=month, working_date__lt=next_month,
                                          mission__nature="PROD", mission__billing_mode="TIME_SPENT")
    if mine:
        timesheets = timesheets.filter(Q(mission__lead__responsible=billing_consultant) | Q(mission__responsible=billing_consultant))
    timesheet_data = timesheets.order_by("mission__lead", "consultant").values_list("mission", "consultant").annotate(Sum("charge"))
    for mission_id, consultant_id, charge in timesheet_data:
        mission = Mission.objects.select_related("lead").get(id=mission_id)
        if mission.lead:
            lead = mission.lead
        else:
            # Bad data, mission with nature prod without lead... This should not happened
            continue
        consultant = Consultant.objects.get(id=consultant_id)
        if not mission in rates:
            rates[mission] = mission.consultant_rates()
        if not lead in timeSpentBilling:
            timeSpentBilling[lead] = [0.0, {}]  # Lead Total and dict of mission
        if not mission in timeSpentBilling[lead][1]:
            timeSpentBilling[lead][1][mission] = [0.0, []]  # Mission Total and detail per consultant
        total = charge * rates[mission][consultant][0]
        timeSpentBilling[lead][0] += total
        timeSpentBilling[lead][1][mission][0] += total
        timeSpentBilling[lead][1][mission][1].append([consultant, to_int_or_round(charge, 2), rates[mission][consultant][0], total, timesheet_ok.get(consultant_id, True)])

    # Sort data
    timeSpentBilling = timeSpentBilling.items()
    timeSpentBilling.sort(key=lambda x: x[0].deal_id)

    return render(request, "billing/pre_billing.html",
                  {"time_spent_billing": timeSpentBilling,
                   "fixed_price_missions": fixedPriceMissions,
                   "undefined_billing_mode_missions": undefinedBillingModeMissions,
                   "month": month,
                   "mine": mine,
                   "user": request.user})
Пример #3
0
def pre_billing(request, year=None, month=None, mine=False):
    """Pre billing page: help to identify bills to send"""
    if year and month:
        month = date(int(year), int(month), 1)
    else:
        month = previousMonth(date.today())

    next_month = nextMonth(month)
    timeSpentBilling = {
    }  # Key is lead, value is total and dict of mission(total, Mission billingData)
    rates = {}  # Key is mission, value is Consultant rates dict

    try:
        billing_consultant = Consultant.objects.get(
            trigramme__iexact=request.user.username)
    except Consultant.DoesNotExist:
        billing_consultant = None
        mine = False

    # Check consultant timesheet to hint if billing could be done based on a clean state
    timesheet_ok = {}
    for consultant in Consultant.objects.filter(active=True,
                                                subcontractor=False):
        missions = consultant.timesheet_missions(month=month)
        timesheetData, timesheetTotal, warning = gatherTimesheetData(
            consultant, missions, month)
        days = sum([v for (k, v) in timesheetTotal.items() if k != "ticket"
                    ])  # Compute timesheet days. Remove lunch ticket count
        if days == working_days(month, holidayDays(month=month)):
            timesheet_ok[consultant.id] = True
        else:
            timesheet_ok[consultant.id] = False

    fixedPriceMissions = Mission.objects.filter(
        nature="PROD",
        billing_mode="FIXED_PRICE",
        timesheet__working_date__gte=month,
        timesheet__working_date__lt=next_month)
    undefinedBillingModeMissions = Mission.objects.filter(
        nature="PROD",
        billing_mode=None,
        timesheet__working_date__gte=month,
        timesheet__working_date__lt=next_month)
    if mine:
        fixedPriceMissions = fixedPriceMissions.filter(
            Q(lead__responsible=billing_consultant)
            | Q(responsible=billing_consultant))
        undefinedBillingModeMissions = undefinedBillingModeMissions.filter(
            Q(lead__responsible=billing_consultant)
            | Q(responsible=billing_consultant))

    fixedPriceMissions = fixedPriceMissions.order_by("lead").distinct()
    undefinedBillingModeMissions = undefinedBillingModeMissions.order_by(
        "lead").distinct()

    timesheets = Timesheet.objects.filter(working_date__gte=month,
                                          working_date__lt=next_month,
                                          mission__nature="PROD",
                                          mission__billing_mode="TIME_SPENT")
    if mine:
        timesheets = timesheets.filter(
            Q(mission__lead__responsible=billing_consultant)
            | Q(mission__responsible=billing_consultant))
    timesheet_data = timesheets.order_by(
        "mission__lead",
        "consultant").values_list("mission",
                                  "consultant").annotate(Sum("charge"))
    for mission_id, consultant_id, charge in timesheet_data:
        mission = Mission.objects.select_related("lead").get(id=mission_id)
        if mission.lead:
            lead = mission.lead
        else:
            # Bad data, mission with nature prod without lead... This should not happened
            continue
        consultant = Consultant.objects.get(id=consultant_id)
        if not mission in rates:
            rates[mission] = mission.consultant_rates()
        if not lead in timeSpentBilling:
            timeSpentBilling[lead] = [0.0,
                                      {}]  # Lead Total and dict of mission
        if not mission in timeSpentBilling[lead][1]:
            timeSpentBilling[lead][1][mission] = [
                0.0, []
            ]  # Mission Total and detail per consultant
        total = charge * rates[mission][consultant][0]
        timeSpentBilling[lead][0] += total
        timeSpentBilling[lead][1][mission][0] += total
        timeSpentBilling[lead][1][mission][1].append([
            consultant,
            to_int_or_round(charge, 2), rates[mission][consultant][0], total,
            timesheet_ok.get(consultant_id, True)
        ])

    # Sort data
    timeSpentBilling = timeSpentBilling.items()
    timeSpentBilling.sort(key=lambda x: x[0].deal_id)

    return render(
        request, "billing/pre_billing.html", {
            "time_spent_billing": timeSpentBilling,
            "fixed_price_missions": fixedPriceMissions,
            "undefined_billing_mode_missions": undefinedBillingModeMissions,
            "month": month,
            "mine": mine,
            "user": request.user
        })
Пример #4
0
def warnForImcompleteTimesheet(warnSurbooking=False, days=None, month=None):
    """Warn users and admin for incomplete timesheet after due date
    @param warnSurbooking: Warn for surbooking days (default is false)
    @param day: only check n first days. If none, check all month"""
    emailTemplate = get_template("batch/timesheet_warning_email.txt")
    if month == "current":
        nextMonth = (date.today().replace(day=1) + timedelta(days=40)).replace(day=1)
        currentMonth = date.today().replace(day=1)
    else:
        # Checking past month
        nextMonth = date.today().replace(day=1)
        currentMonth = (nextMonth - timedelta(days=5)).replace(day=1)

    mails = []  # List of mail to be sent
    for consultant in Consultant.objects.filter(active=True, subcontractor=False):
        recipients = []
        if not [m for m in consultant.forecasted_missions(currentMonth) if m.nature == "PROD"]:
            # No productive mission forecasted on current month
            # Consultant may have just started
            # No check needed. Skip it
            continue
        missions = consultant.timesheet_missions(month=currentMonth)
        timesheetData, timesheetTotal, warning = gatherTimesheetData(consultant, missions, currentMonth)
        url = pydici.settings.PYDICI_HOST + urlresolvers.reverse("people.views.consultant_home", args=[consultant.id])
        url += "?year=%s;month=%s" % (currentMonth.year, currentMonth.month)

        # Truncate if day parameter was given
        if days:
            warning = warning[:days]
        warning = [i for i in warning if i]  # Remove None
        if sum(warning) > 0:
            surbookingDays = warning.count(1)
            incompleteDays = warning.count(2)
            if not warnSurbooking and not incompleteDays:
                continue  # Don't cry if user only have surbooking issue

            user = consultant.getUser()
            if user and user.email:
                recipients.append(user.email)
            if consultant.manager:
                managerUser = consultant.manager.getUser()
                if managerUser and managerUser.email:
                    recipients.append(managerUser.email)

            if recipients:
                msgText = emailTemplate.render(Context(
                                            {"month": currentMonth,
                                             "surbooking_days": surbookingDays,
                                             "incomplete_days": incompleteDays,
                                             "consultant": consultant,
                                             "days": days,
                                             "url": url}))
                mails.append(((_("[pydici] Your timesheet is not correct"), msgText,
                          pydici.settings.LEADS_MAIL_FROM, recipients)))
            else:
                mails.append(((_("[pydici] User has no email"),
                               _("User %s has an incomplete timesheet but cannot be warned because he has no email." % consultant),
                          pydici.settings.LEADS_MAIL_FROM, [pydici.settings.LEADS_MAIL_FROM, ])))

    # Send all emails in one time
    send_mass_mail(mails, fail_silently=False)