def test_mission_timesheet(self): self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD) current_month = date.today().replace(day=1) next_month = nextMonth(current_month) previous_month = previousMonth(current_month) lead = Lead.objects.get(id=1) c1 = Consultant.objects.get(id=1) c2 = Consultant.objects.get(id=2) mission = Mission(lead=lead, subsidiary_id=1, billing_mode="TIME_SPENT", nature="PROD", probability=100) mission.save() response = self.client.get(urlresolvers.reverse("staffing.views.mission_timesheet", args=[mission.id,]), follow=True, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(response.status_code, 200) self.assertEqual(response.context["margin"], 0) self.assertEqual(response.context["objective_margin_total"], 0) self.assertEqual(response.context["forecasted_unused"], 0) self.assertEqual(response.context["current_unused"], 0) self.assertEqual(response.context["avg_daily_rate"], 0) # Add some forecast Staffing(mission=mission, staffing_date=current_month, consultant=c1, charge=15).save() Staffing(mission=mission, staffing_date=current_month, consultant=c2, charge=10).save() Staffing(mission=mission, staffing_date=next_month, consultant=c1, charge=8).save() Staffing(mission=mission, staffing_date=next_month, consultant=c2, charge=6).save() # Add some timesheet - we fake with all charge on the first day Timesheet(mission=mission, working_date=previous_month, consultant=c1, charge=8).save() Timesheet(mission=mission, working_date=previous_month, consultant=c2, charge=5).save() Timesheet(mission=mission, working_date=current_month, consultant=c1, charge=11).save() Timesheet(mission=mission, working_date=current_month, consultant=c2, charge=9).save() # Define objective rates for consultants RateObjective(consultant=c1, start_date=previous_month, daily_rate=700).save() RateObjective(consultant=c2, start_date=previous_month, daily_rate=1050).save() # Add financial conditions for this mission FinancialCondition(consultant=c1, mission=mission, daily_rate=800).save() FinancialCondition(consultant=c2, mission=mission, daily_rate=1100).save() # Define mission price mission.price = 50 mission.save() # Let's test if computation are rights response = self.client.get(urlresolvers.reverse("staffing.views.mission_timesheet", args=[mission.id,]), follow=True, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(response.status_code, 200) self.assertEqual(response.context["margin"], 0) # That's because we are in fixed price self.assertEqual(response.context["objective_margin_total"], 2600) self.assertEqual(response.context["forecasted_unused"], 2.1) self.assertEqual(response.context["current_unused"], 19.4) # Switch to fixed price mission mission.billing_mode = "FIXED_PRICE" mission.save() response = self.client.get(urlresolvers.reverse("staffing.views.mission_timesheet", args=[mission.id,]), follow=True, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(response.status_code, 200) self.assertEqual(response.context["margin"], 2.1) self.assertEqual(response.context["objective_margin_total"], 2600) self.assertEqual(response.context["forecasted_unused"], 0) # Unused is margin in fixes price :-) self.assertEqual(response.context["current_unused"], 0) # idem # Check mission data main table data = response.context["mission_data"] self.assertListEqual(data[0], [c2, [5, 9, 14, 15.4], [1, 6, 7, 7.7], [21, 23.1]]) self.assertListEqual(data[1], [c1, [8, 11, 19, 15.2], [4, 8, 12, 9.6], [31, 24.8]]) self.assertListEqual(data[2], [None, [13, 20, 33, 30.6], [5, 14, 19, 17.3], [52, 47.9], [11.9, 18.7], [4.3, 13], [915.4, 935, 927.3], [860, 928.6, 910.5]])
def compute_automatic_staffing(mission, mode, duration, user=None): """Compute staffing for a given mission. Mode can be after (current staffing) for replace (erase and create)""" now = datetime.now().replace(microsecond=0) # Remove useless microsecond current_month = date.today().replace(day=1) start_date = current_month total = 0 if not mission.consultants(): # no consultant, no staffing. Come on. return if mode=="replace": mission.staffing_set.all().delete() cache.delete("Mission.forecasted_work%s" % mission.id) cache.delete("Mission.done_work%s" % mission.id) if mission.lead: start_date = max(current_month, mission.lead.start_date.replace(day=1)) else: max_staffing = Staffing.objects.filter(mission=mission).aggregate(Max("staffing_date"))["staffing_date__max"] if max_staffing: start_date = max(current_month, nextMonth(max_staffing)) if mission.start_date: start_date = max(start_date, mission.start_date) margin = mission.remaining(mode="target") rates = mission.consultant_rates() rates_sum = sum([i[0] for i in rates.values()]) days = margin*1000 / rates_sum / duration days = max(floor(days * 4) / 4, 0.25) for consultant in rates.keys(): month = start_date for i in range(duration): if total > margin*1000: break if mission.end_date and month > mission.end_date: break s = Staffing(mission=mission, consultant=consultant, charge=days, staffing_date=month, update_date = now) if user: s.last_user = str(user) s.save() total += days * rates[consultant][0] month = nextMonth(month)
def solver_apply_forecast(solver, staffing, consultants, missions, staffing_dates, user): """Apply solver solution to staffing forecast""" now = datetime.now().replace(microsecond=0) # Remove useless microsecond for mission in missions: mission_id = mission.mission_id() # Remove previous staffing for this mission after first month Staffing.objects.filter( mission=mission, staffing_date__gte=staffing_dates[0][0]).delete() # Create new staffing according to solver solution for consultant in consultants: for month in staffing_dates: charge = solver.Value( staffing[consultant.trigramme][mission_id][month[1]]) if charge > 0: s = Staffing(mission=mission, consultant=consultant, staffing_date=month[0], charge=charge, update_date=now, last_user=str(user)) s.save()
def compute_automatic_staffing(mission, mode, duration, user=None): """Compute staffing for a given mission. Mode can be after (current staffing) for replace (erase and create)""" now = datetime.now().replace(microsecond=0) # Remove useless microsecond current_month = date.today().replace(day=1) start_date = current_month total = 0 if mode=="replace": mission.staffing_set.all().delete() cache.delete("Mission.forecasted_work%s" % mission.id) cache.delete("Mission.done_work%s" % mission.id) if mission.lead: start_date = max(current_month, mission.lead.start_date.replace(day=1)) else: max_staffing = Staffing.objects.filter(mission=mission).aggregate(Max("staffing_date"))["staffing_date__max"] if max_staffing: start_date = max(current_month, nextMonth(max_staffing)) margin = mission.margin(mode="target") rates = mission.consultant_rates() rates_sum = sum([i[0] for i in rates.values()]) days = margin*1000 / rates_sum / duration days = max(floor(days * 4) / 4, 0.25) for consultant in rates.keys(): month = start_date for i in range(duration): if total > margin*1000: break s = Staffing(mission=mission, consultant=consultant, charge=days, staffing_date=month, update_date = now) if user: s.last_user = str(user) s.save() total += days * rates[consultant][0] month = nextMonth(month)