class ServiceWorkOrder(models.Model): STATUS_CHOICES = [('requested', 'Requested'), ('progress', 'In progress'), ('completed', 'Completed'), ('authorized', 'Authorized'), ('declined', 'Declined')] date = models.DateField() time = models.TimeField( choices=time_choices('06:00:00', '18:30:00', '00:30:00')) # for services done within the organization internal = models.BooleanField(default=False) works_request = models.ForeignKey('services.workorderrequest', blank=True, null=True, on_delete=models.SET_NULL) description = models.TextField(blank=True, default="") completed = models.DateTimeField(null=True, blank=True) expected_duration = models.DurationField(choices=time_choices('00:00:00', '08:00:00', '00:30:00', delta=True), null=True, blank=True) service_people = models.ManyToManyField('services.ServicePerson', blank=True) team = models.ForeignKey('services.ServiceTeam', on_delete=models.SET_NULL, null=True, blank=True) status = models.CharField(max_length=16, choices=STATUS_CHOICES, blank=True) authorized_by = models.ForeignKey( 'employees.Employee', on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=Q(user__isnull=False)) #filter queryset notes = models.ManyToManyField('common_data.note') progress = models.CharField(max_length=512, blank=True, default="") def __str__(self): return "WO{}".format(self.pk) #TODO string padding @property def procedure_pk(self): if self.works_request.service.procedure: return self.works_request.service.procedure.pk return None def save(self, *args, **kwargs): super().save(*args, **kwargs) if self.works_request: self.works_request.update_status() @property def number_of_employees(self): direct = self.service_people.all().count() team = 0 if self.team: team = self.team.members.all().count() return direct + team @property def expenses(self): return self.workorderexpense_set.all() @property def time_logs(self): # may decide to remove the .all() and use a filter of uses timesheet return self.timelog_set.all() @property def progress_list(self): pl = self.progress.split(",") if self.progress != "" else [] return [int(i) for i in pl] @property def progress_percentage(self): if not self.works_request.service.procedure or \ self.works_request.service.procedure.steps.count() ==0: return 100 total_steps = self.works_request.service.procedure.steps.count() progress = len(self.progress_list) return int(float(progress) * 100.0 / float(total_steps)) @property def total_normal_time(self): return reduce(lambda x, y: x + y, [i.normal_time \ for i in self.time_logs], datetime.timedelta(seconds=0)) @property def total_overtime(self): return reduce(lambda x, y: x + y, [i.overtime for i in self.time_logs], datetime.timedelta(seconds=0)) def get_absolute_url(self): return reverse("services:work-order-detail", kwargs={"pk": self.pk})
def test_time_choices(self): output = time_choices('06:00:00', '12:00:00', '00:30:00') self.assertEqual(len(output), 12) time = datetime.datetime.strptime('11:30:00', "%H:%M:%S").time() self.assertEqual(output[11][0], time)
class Event(models.Model): REMINDER_CHOICES = [ (datetime.timedelta(seconds=0), 'At event start'), (datetime.timedelta(minutes=15), '15 min before'), (datetime.timedelta(hours=1), '1 hour before'), (datetime.timedelta(hours=3), '3 hour before'), (datetime.timedelta(hours=6), '6 hours before'), (datetime.timedelta(days=1), '1 Day before'), (datetime.timedelta(days=3), '3 Days before'), (datetime.timedelta(days=7), '1 week before'), (datetime.timedelta(days=14), '2 weeks before'), (datetime.timedelta(days=30), '1 month before') ] TIME_CHOICES = time_choices('06:00:00','18:00:00','00:30:00') PRIORITY_CHOICES = [ ('normal', 'Normal'), ('high', 'High'), ('low', 'Low') ] ICON_CHOICES = [ ('file-chart-line', 'Report'), ('truck', 'Delivery'), ('users', 'Meeting'), ('stopwatch', 'Deadline'), ('book', 'Training'), ('calendar', 'Event') ] REPEAT_CHOICES = [ (0, 'Never'), (1, 'Daily'), (2, 'Weekly'), (3, 'Monthly'), (4, 'Annually'), ] date = models.DateField() reminder = models.DurationField(choices=REMINDER_CHOICES, default=datetime.timedelta(seconds=0)) completed = models.BooleanField(default=False, blank=True) completion_time = models.DateTimeField(null=True, blank=True) start_time = models.TimeField(choices=TIME_CHOICES, default="06:00:00") end_time = models.TimeField(choices=TIME_CHOICES, default="06:00:00") priority = models.CharField(max_length=8, choices=PRIORITY_CHOICES, default='normal') description = models.TextField(blank=True) repeat = models.PositiveSmallIntegerField(default=0, choices=REPEAT_CHOICES) repeat_active = models.BooleanField(default=False, blank=True) label = models.CharField(max_length=32, blank=True) icon = models.CharField(max_length=32, blank=True, choices=ICON_CHOICES) owner = models.ForeignKey('auth.User', on_delete=models.SET_NULL, null=True) reminder_notification = models.ForeignKey('messaging.notification', blank=True, null=True, on_delete=models.SET_NULL) def get_absolute_url(self): return reverse("planner:event-detail", kwargs={"pk": self.pk}) @property def participants(self): return EventParticipant.objects.filter(event=self) def add_participant(self, evt_type, pk): evt_mapping = { 'supplier': 2, 'employee': 0, 'customer': 1 } evt_type = evt_mapping[evt_type] participant = None if evt_type == 0: participant = EventParticipant.objects.create( event=self, participant_type = evt_type, employee=Employee.objects.get(pk=pk) ) elif evt_type == 1: participant = EventParticipant.objects.create( event=self, participant_type = evt_type, customer=Customer.objects.get(pk=pk) ) elif evt_type == 2: participant = EventParticipant.objects.create( event=self, participant_type = evt_type, supplier=inventory.models.Supplier.objects.get(pk=pk) ) else: raise Exception('no type was specified') return participant def complete(self): self.completed = True self.completion_time = datetime.datetime.now() self.save() @property def repeat_string(self): mapping = dict(self.REPEAT_CHOICES) return mapping[self.repeat] def repeat_on_date(self, date): # eliminate past dates at the begining if self.date > date: return False if self.repeat == 0: return False elif self.repeat == 1: return True elif self.repeat == 2: if self.date.weekday() == date.weekday(): return True return False elif self.repeat == 3: if self.date.day == date.day: return True return False elif self.repeat == 4: if self.date.day == date.day and self.date.month == date.month: return True return False return False def __str__(self): return self.label
class ServiceWorkOrder(models.Model): STATUS_CHOICES = [('requested', 'Requested'), ('progress', 'In progress'), ('completed', 'Completed'), ('authorized', 'Authorized'), ('declined', 'Declined')] date = models.DateField() time = models.TimeField( choices=time_choices('00:00:00', '23:30:00', '00:30:00')) # for services done within the organization internal = models.BooleanField(default=False) works_request = models.ForeignKey('services.workorderrequest', blank=True, null=True, on_delete=models.SET_NULL) description = models.TextField(blank=True, default="") completed = models.DateTimeField(null=True, blank=True) expected_duration = models.DurationField(choices=time_choices('00:00:00', '23:30:00', '00:30:00', delta=True), null=True, blank=True) service_people = models.ManyToManyField('services.ServicePerson', limit_choices_to=Q(active=True), blank=True) team = models.ForeignKey('services.ServiceTeam', on_delete=models.SET_NULL, null=True, blank=True) status = models.CharField(max_length=16, choices=STATUS_CHOICES, blank=True) authorized_by = models.ForeignKey( 'employees.Employee', on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=Q(user__isnull=False)) # filter queryset notes = models.ManyToManyField('common_data.note') def __str__(self): return "WO{}".format(self.pk) # TODO string padding @property def procedure_pk(self): if self.works_request.service.procedure: return self.works_request.service.procedure.pk return None @property def completed_tasks(self): return self.workordertask_set.filter(completed=True) def save(self, *args, **kwargs): super().save(*args, **kwargs) if self.works_request: self.works_request.update_status() procedure = self.works_request.service.procedure if procedure and self.workordertask_set.all().count() == 0: due = datetime.date.today() if self.works_request.invoice: due = self.works_request.invoice.due for step in procedure.steps: WorkOrderTask.objects.create( work_order=self, description=step.description, due=due, start=due, ) @property def number_of_employees(self): direct = self.service_people.all().count() team = 0 if self.team: team = self.team.members.all().count() return direct + team @property def expenses(self): return self.workorderexpense_set.all() @property def unbilled_expenses(self): return self.workorderexpense_set.filter(expense__billable=False) @property def time_logs(self): # may decide to remove the .all() and use a filter of uses timesheet return self.timelog_set.all() @property def total_normal_time(self): return reduce(lambda x, y: x + y, [i.normal_time for i in self.time_logs], datetime.timedelta(seconds=0)) @property def total_overtime(self): return reduce(lambda x, y: x + y, [i.overtime for i in self.time_logs], datetime.timedelta(seconds=0)) def get_absolute_url(self): return reverse("services:work-order-detail", kwargs={"pk": self.pk}) @property def status_string(self): return dict(self.STATUS_CHOICES)[self.status] @property def consumables_used(self): return ConsumablesRequisitionLine.objects.filter( requisition__work_order=self) @property def total_expenses(self): return sum([i.expense.amount for i in self.expenses]) @property def total_time(self): return self.total_overtime + self.total_normal_time