class UserPreference(models.Model): class UserPreferenceManager(models.Manager): def get_by_natural_key(self, usr, prop): return self.get(user=usr, property=prop) objects = UserPreferenceManager() id = models.AutoField(_('identifier'), primary_key=True) # Translators: Translation included with Django user = models.ForeignKey(User, verbose_name=_('user'), blank=False, null=True, editable=False, related_name='preferences') property = models.CharField(max_length=100, blank=False, null=False) value = JSONBField(max_length=1000, blank=False, null=False) def natural_key(self): return (self.user, self.property) class Meta: db_table = "common_preference" unique_together = (('user', 'property'), ) verbose_name = 'preference' verbose_name_plural = 'preferences'
class ScheduledTask(models.Model): # Database fields name = models.CharField("name", primary_key=True, max_length=300, db_index=True) next_run = models.DateTimeField("nextrun", blank=True, null=True, db_index=True) user = models.ForeignKey( User, blank=False, null=True, editable=False, related_name="schedules", on_delete=models.CASCADE, ) email_failure = models.CharField("email_failure", max_length=300, null=True, blank=True) email_success = models.CharField("email_success", max_length=300, null=True, blank=True) data = JSONBField(default="{}", null=True, blank=True) def __str__(self): return self.name class Meta: db_table = "execute_schedule" verbose_name_plural = _("scheduled tasks") verbose_name = _("scheduled task") def computeNextRun(self, now=None): if now: self.next_run = now + timedelta(seconds=600) else: self.next_run = datetime.now() + timedelta(seconds=600) def save(self, *args, **kwargs): self.computeNextRun() super().save(*args, **kwargs)
def add_extra_model_fields(sender, **kwargs): model_path = "%s.%s" % (sender.__module__, sender.__name__) for field_name, label, fieldtype, editable, initially_hidden in _register.get( model_path, []): register_args = (_register_kwargs[(model_path, field_name)] if (model_path, field_name) in _register_kwargs else None) if fieldtype == "string": if register_args and "max_length" in register_args: max_length = register_args["max_length"] else: max_length = 300 field = models.CharField( label, max_length=max_length, null=True, blank=True, db_index=True, editable=editable, ) elif fieldtype == "boolean": field = models.NullBooleanField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "number": # Note: Other numeric fields have precision 20, 8. # Changing the value below would require migrating existing attributes of all projects. field = models.DecimalField( label, max_digits=15, decimal_places=6, null=True, blank=True, db_index=True, editable=editable, ) elif fieldtype == "integer": field = models.IntegerField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "date": field = models.DateField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "datetime": field = models.DateTimeField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "duration": field = models.DurationField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "time": field = models.TimeField(label, null=True, blank=True, db_index=True, editable=editable) elif fieldtype == "jsonb": field = JSONBField(default="{}", null=True, blank=True, editable=editable) else: raise ImproperlyConfigured("Invalid attribute type '%s'." % fieldtype) field.contribute_to_class(sender, field_name)
class ScheduledTask(models.Model): # Database fields name = models.CharField("name", primary_key=True, max_length=300, db_index=True) next_run = models.DateTimeField("nextrun", blank=True, null=True, db_index=True) user = models.ForeignKey( User, blank=False, null=True, editable=False, related_name="schedules", on_delete=models.CASCADE, ) email_failure = models.CharField("email_failure", max_length=300, null=True, blank=True) email_success = models.CharField("email_success", max_length=300, null=True, blank=True) data = JSONBField(null=True, blank=True) def __str__(self): return self.name class Meta: db_table = "execute_schedule" verbose_name_plural = _("scheduled tasks") verbose_name = _("scheduled task") def computeNextRun(self, now=None): if self.data: weekdays = [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", ] starttime = self.data.get("starttime", -1) if starttime is not None and starttime >= 0: starttime = int(starttime) if not now: now = datetime.now() time_of_day = now.hour * 3600 + now.minute * 60 + now.second weekday = now.weekday() # Loop over current + next 7 days for n in range(8): if n == 0 and time_of_day > starttime: # Too late to start today weekday = (weekday + 1) % 7 elif self.data.get(weekdays[weekday], False): self.next_run = (now + timedelta(days=n)).replace( hour=int(starttime / 3600), minute=int(int(starttime % 3600) / 60), second=starttime % 60, microsecond=0, ) return else: weekday = (weekday + 1) % 7 self.next_run = None def adjustForTimezone(self, offset): if isinstance(offset, timedelta): offset = int(offset.total_seconds()) self.next_run += timedelta(seconds=offset) self.data["starttime"] = self.data.get("starttime", 0) + offset if self.data["starttime"] < 0: # Starts the previous day! self.data["starttime"] += 24 * 3600 tmp = self.data.get("monday", False) self.data["monday"] = self.data.get("tuesday", False) self.data["tuesday"] = self.data.get("wednesday", False) self.data["wednesday"] = self.data.get("thursday", False) self.data["thursday"] = self.data.get("friday", False) self.data["friday"] = self.data.get("saturday", False) self.data["saturday"] = self.data.get("sunday", False) self.data["sunday"] = tmp elif self.data["starttime"] > 24 * 3600: # Starts the next day! self.data["starttime"] -= 24 * 3600 tmp = self.data.get("sunday", False) self.data["sunday"] = self.data.get("saturday", False) self.data["saturday"] = self.data.get("friday", False) self.data["friday"] = self.data.get("thursday", False) self.data["thursday"] = self.data.get("wednesday", False) self.data["wednesday"] = self.data.get("tuesday", False) self.data["tuesday"] = self.data.get("monday", False) self.data["monday"] = tmp return self def save(self, *args, **kwargs): self.computeNextRun() super().save(*args, **kwargs)
class Demand(AuditModel, HierarchyModel): # Status demandstatus = ( ('inquiry', _('inquiry')), ('quote', _('quote')), ('open', _('open')), ('closed', _('closed')), ('canceled', _('canceled')), ) # Database fields description = models.CharField( _('description'), max_length=500, null=True, blank=True ) category = models.CharField( _('category'), max_length=300, null=True, blank=True, db_index=True ) subcategory = models.CharField( _('subcategory'), max_length=300, null=True, blank=True, db_index=True ) customer = models.ForeignKey( Customer, verbose_name=_('customer'), db_index=True, on_delete=models.CASCADE ) item = models.ForeignKey( Item, verbose_name=_('item'), db_index=True, on_delete=models.CASCADE ) location = models.ForeignKey( Location, verbose_name=_('location'), db_index=True, on_delete=models.CASCADE ) due = models.DateTimeField(_('due'), help_text=_('Due date of the demand'), db_index=True) status = models.CharField( _('status'), max_length=10, null=True, blank=True, choices=demandstatus, default='open', help_text=_('Status of the demand. Only "open" demands are planned'), ) operation = models.ForeignKey( Operation, verbose_name=_('delivery operation'), null=True, blank=True, related_name='used_demand', on_delete=models.CASCADE, help_text=_('Operation used to satisfy this demand') ) quantity = models.DecimalField( _('quantity'), max_digits=20, decimal_places=8, default=1 ) priority = models.IntegerField( _('priority'), default=10, help_text=_('Priority of the demand (lower numbers indicate more important demands)') ) minshipment = models.DecimalField( _('minimum shipment'), null=True, blank=True, max_digits=20, decimal_places=8, help_text=_('Minimum shipment quantity when planning this demand') ) maxlateness = models.DurationField( _('maximum lateness'), null=True, blank=True, help_text=_("Maximum lateness allowed when planning this demand") ) delay = models.DurationField( _('delay'), null=True, blank=True, editable=False ) plannedquantity = models.DecimalField( _('planned quantity'), max_digits=20, decimal_places=8, null=True, blank=True, editable=False, help_text=_('Quantity planned for delivery') ) deliverydate = models.DateTimeField( _('delivery date'), help_text=_('Delivery date of the demand'), null=True, blank=True, editable=False, ) plan = JSONBField(default="{}", null=True, blank=True, editable=False) # Convenience methods def __str__(self): return self.name class Meta(AuditModel.Meta): db_table = 'demand' verbose_name = _('sales order') verbose_name_plural = _('sales orders') ordering = ['name']
class OperationPlan(AuditModel): # Possible types types = ( ('STCK', _('inventory')), ('MO', _('manufacturing order')), ('PO', _('purchase order')), ('DO', _('distribution order')), ('DLVR', _('delivery order')), ) # Possible status orderstatus = ( ('proposed', _('proposed')), ('approved', _('approved')), ('confirmed', _('confirmed')), ('closed', _('closed')), ) # Database fields # Common fields id = models.AutoField( _('identifier'), primary_key=True, help_text=_('Unique identifier of an operationplan') ) status = models.CharField( _('status'), null=True, blank=True, max_length=20, choices=orderstatus, help_text=_('Status of the order') ) type = models.CharField( _('type'), max_length=5, choices=types, default='MO', help_text=_('Order type'), db_index=True ) reference = models.CharField( _('reference'), max_length=300, null=True, blank=True, help_text=_('External reference of this order') ) quantity = models.DecimalField( _('quantity'), max_digits=20, decimal_places=8, default='1.00' ) color = models.DecimalField( _('color'), max_digits=20, null=True, blank=True, decimal_places=8, default='0.00' ) startdate = models.DateTimeField( _('start date'), help_text=_('start date'), null=True, blank=True, db_index=True ) enddate = models.DateTimeField( _('end date'), help_text=_('end date'), null=True, blank=True, db_index=True ) criticality = models.DecimalField( _('criticality'), max_digits=20, decimal_places=8, null=True, blank=True, editable=False ) delay = models.DurationField( _('delay'), null=True, blank=True, editable=False ) plan = JSONBField(default="{}", null=True, blank=True, editable=False) # Used only for manufacturing orders operation = models.ForeignKey( Operation, verbose_name=_('operation'), db_index=True, null=True, blank=True, on_delete=models.CASCADE ) owner = models.ForeignKey( 'self', verbose_name=_('owner'), null=True, blank=True, related_name='xchildren', help_text=_('Hierarchical parent'), on_delete=models.CASCADE ) # Used for purchase orders and distribution orders item = models.ForeignKey( Item, verbose_name=_('item'), on_delete=models.CASCADE, null=True, blank=True, db_index=True ) # Used only for distribution orders origin = models.ForeignKey( Location, verbose_name=_('origin'), null=True, blank=True, related_name='origins', db_index=True, on_delete=models.CASCADE ) destination = models.ForeignKey( Location, verbose_name=_('destination'), on_delete=models.CASCADE, null=True, blank=True, related_name='destinations', db_index=True ) # Used only for purchase orders supplier = models.ForeignKey( Supplier, verbose_name=_('supplier'), null=True, blank=True, db_index=True, on_delete=models.CASCADE ) location = models.ForeignKey( Location, verbose_name=_('location'), null=True, blank=True, db_index=True, on_delete=models.CASCADE ) # Used for delivery operationplans demand = models.ForeignKey( Demand, verbose_name=_('demand'), null=True, blank=True, db_index=True, on_delete=models.CASCADE ) due = models.DateTimeField( _('due'), help_text=_('Due date of the demand/forecast'), null=True, blank=True, editable=False ) name = models.CharField( _('name'), max_length=1000, null=True, blank=True, db_index=True ) class Manager(MultiDBManager): def get_by_natural_key(self, reference): # Note: we are not enforcing the uniqueness of this natural key in the database return self.get(reference=reference) def natural_key(self): return (self.reference) objects = Manager() natural_key = ('reference',) def __str__(self): return str(self.id) def save(self, *args, **kwargs): if not self.id: if 'using' in kwargs: db = kwargs['using'] else: state = getattr(self, '_state', None) db = state.db if state else DEFAULT_DB_ALIAS self.id = OperationPlan.objects.all().using(db).aggregate(Max('id'))['id__max'] if self.id: self.id += 1 else: self.id = 1 kwargs['force_insert'] = True # Call the real save() method super(OperationPlan, self).save(*args, **kwargs) class Meta(AuditModel.Meta): db_table = 'operationplan' verbose_name = _('operationplan') verbose_name_plural = _('operationplans') ordering = ['id']