Esempio n. 1
0
class Errata(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    # TODO: If we seperate the Errata application from the CMS, the books will need to be store differently. `book` will be removed, `openstax_book` will store as string
    book = models.ForeignKey(Book, on_delete=models.PROTECT)
    openstax_book = models.CharField(max_length=255,
                                     null=True,
                                     blank=True,
                                     editable=False)

    is_assessment_errata = models.CharField(
        max_length=100,
        choices=YES_NO_CHOICES,
        blank=True,
        null=True,
    )
    assessment_id = models.CharField(max_length=255, null=True, blank=True)
    status = models.CharField(
        max_length=100,
        choices=ERRATA_STATUS,
        default=NEW,
    )
    resolution = models.CharField(
        max_length=100,
        choices=ERRATA_RESOLUTIONS,
        blank=True,
        null=True,
    )
    duplicate_id = models.ForeignKey('self',
                                     related_name='duplicate_report',
                                     null=True,
                                     blank=True,
                                     on_delete=models.PROTECT)
    reviewed_date = models.DateField(blank=True, null=True, editable=False)
    corrected_date = models.DateField(blank=True, null=True)
    archived = models.BooleanField(default=False)
    junk = models.BooleanField(
        default=False,
        help_text=
        'Flagging the erratum as junk will automatically flag it for archive as well.'
    )
    location = models.TextField(blank=True, null=True)
    additional_location_information = models.TextField(blank=True, null=True)
    detail = models.TextField()
    resolution_notes = models.TextField(
        blank=True,
        null=True,
        help_text=
        'Leaving the resolution notes blank will allow the field to auto-fill with the appropriate text based on status/resolution selections.'
    )
    resolution_date = models.DateField(blank=True, null=True)
    internal_notes = models.TextField(
        blank=True,
        null=True,
        help_text=
        'Only users with errata admin access can view and edit the contents of this field.'
    )
    error_type = models.CharField(max_length=100,
                                  choices=ERRATA_ERROR_TYPES,
                                  blank=True,
                                  null=True)
    error_type_other = models.CharField(max_length=255, blank=True, null=True)
    number_of_errors = models.PositiveIntegerField(default=1)
    resource = models.CharField(max_length=100,
                                choices=ERRATA_RESOURCES,
                                blank=True,
                                null=True)
    resource_other = models.CharField(max_length=255, blank=True, null=True)

    # TODO: We are keeping the Foreign Key to the local user until the migrations to production are complete, then remove submitted_by and submitter_email_address
    submitted_by = models.ForeignKey(User,
                                     blank=True,
                                     null=True,
                                     on_delete=models.SET_NULL)
    submitter_email_address = models.EmailField(blank=True, null=True)

    submitted_by_account_id = models.IntegerField(
        blank=True,
        null=True,
        validators=[MinValueValidator(0), is_user_blocked])
    accounts_user_email = models.CharField(max_length=255,
                                           null=True,
                                           blank=True)
    accounts_user_name = models.CharField(max_length=255,
                                          null=True,
                                          blank=True)
    accounts_user_faculty_status = models.CharField(max_length=255,
                                                    null=True,
                                                    blank=True)

    file_1 = models.FileField(upload_to='errata/user_uploads/1/',
                              blank=True,
                              null=True)
    file_2 = models.FileField(upload_to='errata/user_uploads/2/',
                              blank=True,
                              null=True)

    # @property
    # def user_email(self):
    #     try:
    #         user = get_user_info(self.submitted_by_account_id)
    #         return user['email']
    #     except:
    #         return None
    #
    # @property
    # def user_name(self):
    #     try:
    #         user = get_user_info(self.submitted_by_account_id)
    #         return user['fullname']
    #     except:
    #         return None
    #
    # @property
    # def user_faculty_status(self):
    #     try:
    #         user = get_user_info(self.submitted_by_account_id)
    #         return user['faculty_status']
    #     except:
    #         return None

    @property
    def accounts_link(self):
        try:
            return format_html(
                '<a href="{}/admin/users/{}/edit" target="_blank">OpenStax Accounts Link</a>'
                .format(settings.ACCOUNTS_URL, self.submitted_by_account_id))
        except:
            return None

    @property
    def short_detail(self):
        return truncatewords(self.detail, 15)

    def clean(self):
        if self.status == 'Completed' and not self.resolution or self.status == 'Reviewed' and not self.resolution:
            raise ValidationError({
                'resolution':
                'Resolution is required if status is completed or reviewed.'
            })
        if (self.status == 'Editorial Review'
                or self.status == 'Andrew Editorial Review' or self.status
                == 'Anthony Editorial Review' or self.status == 'Reviewed' or
                self.status == 'Completed') and not self.is_assessment_errata:
            raise ValidationError({
                'is_assessment_errata':
                'You must specify if this is an assessment errata.'
            })
        if (self.status == 'Completed'
                and self.resolution == 'Duplicate') and not self.duplicate_id:
            raise ValidationError({
                'duplicate_id':
                'You must specify the duplicate report ID when resolution is marked duplicate.'
            })

    def save(self, *args, **kwargs):
        # update instance dates
        if self.resolution:
            self.resolution_date = timezone.now()
        if self.status == "Reviewed":
            self.reviewed_date = timezone.now()
        if self.status == "Completed" and self.resolution != "Will Not Fix":
            self.corrected_date = timezone.now()
            # book updated field is being used front-end to show google the content is being maintained
            Book.objects.filter(pk=self.book.pk).update(updated=now())

        # prefill resolution notes based on certain status and resolutions
        if self.resolution == "Duplicate" and not self.resolution_notes:
            self.resolution_notes = "This is a duplicate of report <a href='https://openstax.org/errata/" + str(
                self.duplicate_id.id) + "'>" + str(
                    self.duplicate_id.id) + "</a>."
        if self.resolution == "Not An Error" and not self.resolution_notes:
            self.resolution_notes = "Our reviewers determined this was not an error."
        if self.resolution == "Will Not Fix" and not self.resolution_notes:
            self.resolution_notes = "Our reviewers determined the textbook meets scope, sequence, and accuracy requirements as is.  No change will be made."
        if self.resolution == "Major Book Revision" and not self.resolution_notes:
            self.resolution_notes = "Our reviewers determined this would require a significant book revision.  While we cannot make this change at this time, we will consider it for future editions of this book."
        if (self.status == "Reviewed" or self.status == "Completed"
            ) and self.resolution == "Approved" and not self.resolution_notes:
            self.resolution_notes = "Our reviewers accepted this change."
        if self.status == "Completed" and self.resolution == "Sent to Customer Support" and not self.resolution_notes:
            self.resolution_notes = "Thank you for this feedback. Your report has been escalated to our Support team. A member of the Support team will contact you with further details."
        if self.resolution == 'Technical Error' and not self.resolution_notes:
            self.resolution_notes = 'This a technical error and the proper departments have been notified so that it can be fixed. Thank you for your submission.'
        if self.resolution == 'More Information Requested' and not self.resolution_notes:
            self.resolution_notes = 'Thank you for the feedback. Unfortunately, our reviewers were unable to locate this error. Please submit a new report with additional information, such as a link to the relevant content, or a screenshot.'
        if self.resolution == 'Partner Product' and not self.resolution_notes:
            self.resolution_notes = 'This issue is occurring on a platform that is not managed by OpenStax. We will make our best efforts to get this resolved; however, we suggest reporting this issue within the platform you are using.'

        #auto filling a couple of fields if it is clear that it is an assessment errata based on the text that tutor fills into the additional_location_information field
        if self.additional_location_information and self.resource == 'OpenStax Tutor' and (
                '@' in self.additional_location_information.split()[0]):
            self.is_assessment_errata = 'Yes'
            self.assessment_id = self.additional_location_information.split(
                '@', )[0]

        # set to archived if user is shadow banned
        if (is_user_shadow_blocked(self.submitted_by_account_id)):
            self.archived = True

        # invalidate cloudfront cache so FE updates (remove when errata is no longer in CMS)
        invalidate_cloudfront_caches()

        super(Errata, self).save(*args, **kwargs)

    @hooks.register('register_admin_menu_item')
    def register_errata_menu_item():
        return MenuItem('Errata',
                        '/django-admin/errata/errata',
                        classnames='icon icon-form',
                        order=10000)

    # @hooks.register('register_admin_menu_item')
    # def register_errata_menu_item():
    #     return MenuItem('Errata (beta)', '/api/errata/admin/dashboard/', classnames='icon icon-form', order=10000)

    def __str__(self):
        return self.book.book_title

    class Meta:
        verbose_name = "erratum"
        verbose_name_plural = "erratum"
Esempio n. 2
0
class Cuenta(models.Model):
	id = models.AutoField(primary_key=True)
	codigo = models.IntegerField()
	nombre = models.CharField(max_length = 256)
	debe = models.DecimalField('debe', max_digits=50, decimal_places=2, blank=False, null=False, validators=[MinValueValidator(0)])
	haber = models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=False, validators=[MinValueValidator(0)])
	saldoDeudor = models.DecimalField('saldo_deudor', max_digits =50, decimal_places=2,blank=False,null=False,validators=[MinValueValidator(0)],default=0.00)
	saldoAcreedor = models.DecimalField('saldo_acreedor', max_digits =50, decimal_places=2,blank=False,null=False,validators=[MinValueValidator(0)],default=0.00)
	codigo_dependiente = models.IntegerField(null= True)
	descripcion = models.CharField(max_length= 256, null=False, blank=False, default='null')
	def __str__(self):
		return '{}{}'.format(self.nombre)

	def getHaber(self):
		return self.haber

	def getDebe(self):
		return self.debe

	def getSaldoAcreedor(self):
		return self.saldoAcreedor

	def getSaldoDeudor(self):
		return self.saldoDeudor
Esempio n. 3
0
class estadoComprobacion(models.Model):
	id= models.AutoField(primary_key=True)
	debe =models.DecimalField('debe', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	haber= models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 4
0
class Salida(models.Model):
	id=models.AutoField(primary_key=True)
	kardex=models.ForeignKey(Kardex, null=True, blank=True, on_delete=models.CASCADE)
	fechaSalida= models.DateField('Fecha de Entrada', help_text='Formato: AAAA/MM/DD', blank=False, null=False)
	cantidadSalida= models.IntegerField()
	costoTotalSalida= models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	costoUnitarioSalida= models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 5
0
class Orden(models.Model):
	id=models.AutoField(primary_key=True)
	pan=models.ForeignKey(Pan, null=True, blank=True, on_delete=models.CASCADE)
	cif=models.ForeignKey(CIF, null=True, blank=True, on_delete=models.CASCADE)
	descripcion=models.CharField(max_length = 256)
	CMOD=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	cantEmpleados= models.IntegerField()
	diasTrabajados=  models.IntegerField()
	fechaCreacion= models.DateField('Fecha de Creacion', help_text='Formato: AAAA/MM/DD', blank=False, null=False)
	fechaEntrega= models.DateField('Fecha de Entrega', help_text='Formato: AAAA/MM/DD', blank=False, null=False)
	CIF_O=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	CMP=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	terminado= models.NullBooleanField(null = False);
Esempio n. 6
0
class Profile(models.Model):
    """User profiles storage."""

    user = models.OneToOneField(User,
                                unique=True,
                                editable=False,
                                on_delete=models.deletion.CASCADE)
    language = models.CharField(
        verbose_name=_("Interface Language"),
        max_length=10,
        blank=True,
        choices=settings.LANGUAGES,
    )
    languages = models.ManyToManyField(
        Language,
        verbose_name=_("Translated languages"),
        blank=True,
        help_text=_("Choose the languages you can translate to. "
                    "These will be offered to you on the dashboard "
                    "for easier access to your chosen translations."),
    )
    secondary_languages = models.ManyToManyField(
        Language,
        verbose_name=_("Secondary languages"),
        help_text=_(
            "Choose languages you can understand, strings in those languages "
            "will be shown in addition to the source string."),
        related_name="secondary_profile_set",
        blank=True,
    )
    suggested = models.IntegerField(default=0, db_index=True)
    translated = models.IntegerField(default=0, db_index=True)
    uploaded = models.IntegerField(default=0, db_index=True)
    commented = models.IntegerField(default=0, db_index=True)

    hide_completed = models.BooleanField(
        verbose_name=_("Hide completed translations on the dashboard"),
        default=False)
    secondary_in_zen = models.BooleanField(
        verbose_name=_("Show secondary translations in the Zen mode"),
        default=True)
    hide_source_secondary = models.BooleanField(
        verbose_name=_("Hide source if a secondary translation exists"),
        default=False)
    editor_link = models.CharField(
        default="",
        blank=True,
        max_length=200,
        verbose_name=_("Editor link"),
        help_text=_(
            "Enter a custom URL to be used as link to the source code. "
            "You can use {{branch}} for branch, "
            "{{filename}} and {{line}} as filename and line placeholders."),
        validators=[validate_editor],
    )
    TRANSLATE_FULL = 0
    TRANSLATE_ZEN = 1
    translate_mode = models.IntegerField(
        verbose_name=_("Translation editor mode"),
        choices=((TRANSLATE_FULL, _("Full editor")), (TRANSLATE_ZEN,
                                                      _("Zen mode"))),
        default=TRANSLATE_FULL,
    )
    ZEN_VERTICAL = 0
    ZEN_HORIZONTAL = 1
    zen_mode = models.IntegerField(
        verbose_name=_("Zen editor mode"),
        choices=(
            (ZEN_VERTICAL, _("Top to bottom")),
            (ZEN_HORIZONTAL, _("Side by side")),
        ),
        default=ZEN_VERTICAL,
    )
    special_chars = models.CharField(
        default="",
        blank=True,
        max_length=30,
        verbose_name=_("Special characters"),
        help_text=_(
            "You can specify additional special visual keyboard characters "
            "to be shown while translating. It can be useful for "
            "characters you use frequently, but are hard to type on your keyboard."
        ),
    )
    nearby_strings = models.SmallIntegerField(
        verbose_name=_("Number of nearby strings"),
        default=settings.NEARBY_MESSAGES,
        validators=[MinValueValidator(1),
                    MaxValueValidator(50)],
        help_text=
        _("Number of nearby strings to show in each direction in the full editor."
          ),
    )
    auto_watch = models.BooleanField(
        verbose_name=_("Automatically watch projects on contribution"),
        default=settings.DEFAULT_AUTO_WATCH,
        help_text=
        _("Whenever you translate a string in a project, you will start watching it."
          ),
    )

    DASHBOARD_WATCHED = 1
    DASHBOARD_COMPONENT_LIST = 4
    DASHBOARD_SUGGESTIONS = 5
    DASHBOARD_COMPONENT_LISTS = 6

    DASHBOARD_CHOICES = (
        (DASHBOARD_WATCHED, _("Watched translations")),
        (DASHBOARD_COMPONENT_LISTS, _("Component lists")),
        (DASHBOARD_COMPONENT_LIST, _("Component list")),
        (DASHBOARD_SUGGESTIONS, _("Suggested translations")),
    )

    DASHBOARD_SLUGS = {
        DASHBOARD_WATCHED: "your-subscriptions",
        DASHBOARD_COMPONENT_LIST: "list",
        DASHBOARD_SUGGESTIONS: "suggestions",
        DASHBOARD_COMPONENT_LISTS: "componentlists",
    }

    dashboard_view = models.IntegerField(
        choices=DASHBOARD_CHOICES,
        verbose_name=_("Default dashboard view"),
        default=DASHBOARD_WATCHED,
    )

    dashboard_component_list = models.ForeignKey(
        "trans.ComponentList",
        verbose_name=_("Default component list"),
        on_delete=models.deletion.SET_NULL,
        blank=True,
        null=True,
    )

    watched = models.ManyToManyField(
        "trans.Project",
        verbose_name=_("Watched projects"),
        help_text=_("You can receive notifications for watched projects and "
                    "they are shown on the dashboard by default."),
        blank=True,
    )

    # Public profile fields
    website = models.URLField(
        verbose_name=_("Website URL"),
        blank=True,
    )
    liberapay = models.SlugField(
        verbose_name=_("Liberapay username"),
        blank=True,
        help_text=_("Liberapay is a platform to donate money to teams, "
                    "organizations and individuals."),
    )
    fediverse = models.URLField(
        verbose_name=_("Fediverse URL"),
        blank=True,
        help_text=_("Link to your Fediverse profile for federated services "
                    "like Mastodon or diaspora*."),
    )
    codesite = models.URLField(
        verbose_name=_("Code site URL"),
        blank=True,
        help_text=_(
            "Link to your code profile for services like Codeberg or GitLab."),
    )
    github = models.SlugField(
        verbose_name=_("GitHub username"),
        blank=True,
    )
    twitter = models.SlugField(
        verbose_name=_("Twitter username"),
        blank=True,
    )
    linkedin = models.SlugField(
        verbose_name=_("LinkedIn profile name"),
        help_text=_(
            "Your LinkedIn profile name from linkedin.com/in/profilename"),
        blank=True,
    )
    location = models.CharField(
        verbose_name=_("Location"),
        max_length=100,
        blank=True,
    )
    company = models.CharField(
        verbose_name=_("Company"),
        max_length=100,
        blank=True,
    )
    public_email = EmailField(
        verbose_name=_("Public e-mail"),
        blank=True,
        max_length=EMAIL_LENGTH,
    )

    def __str__(self):
        return self.user.username

    def get_absolute_url(self):
        return self.user.get_absolute_url()

    def get_user_display(self):
        return get_user_display(self.user)

    def get_user_display_link(self):
        return get_user_display(self.user, True, True)

    def get_user_name(self):
        return get_user_display(self.user, False)

    def increase_count(self, item: str, increase: int = 1):
        """Updates user actions counter."""
        # Update our copy
        setattr(self, item, getattr(self, item) + increase)
        # Update database
        update = {item: F(item) + increase}
        Profile.objects.filter(pk=self.pk).update(**update)

    @property
    def full_name(self):
        """Return user's full name."""
        return self.user.full_name

    def clean(self):
        """Check if component list is chosen when required."""
        # There is matching logic in ProfileBaseForm.add_error to ignore this
        # validation on partial forms
        if (self.dashboard_view == Profile.DASHBOARD_COMPONENT_LIST
                and self.dashboard_component_list is None):
            message = _(
                "Please choose which component list you want to display on "
                "the dashboard.")
            raise ValidationError({
                "dashboard_component_list": message,
                "dashboard_view": message
            })
        if (self.dashboard_view != Profile.DASHBOARD_COMPONENT_LIST
                and self.dashboard_component_list is not None):
            message = _(
                "Selecting component list has no effect when not shown on "
                "the dashboard.")
            raise ValidationError({
                "dashboard_component_list": message,
                "dashboard_view": message
            })

    def dump_data(self):
        def map_attr(attr):
            if attr.endswith("_id"):
                return attr[:-3]
            return attr

        def dump_object(obj, *attrs):
            return {map_attr(attr): getattr(obj, attr) for attr in attrs}

        result = {
            "basic":
            dump_object(self.user, "username", "full_name", "email",
                        "date_joined"),
            "profile":
            dump_object(
                self,
                "language",
                "suggested",
                "translated",
                "uploaded",
                "hide_completed",
                "secondary_in_zen",
                "hide_source_secondary",
                "editor_link",
                "translate_mode",
                "zen_mode",
                "special_chars",
                "dashboard_view",
                "dashboard_component_list_id",
            ),
            "auditlog": [
                dump_object(log, "address", "user_agent", "timestamp",
                            "activity")
                for log in self.user.auditlog_set.iterator()
            ],
        }
        result["profile"]["languages"] = [
            lang.code for lang in self.languages.iterator()
        ]
        result["profile"]["secondary_languages"] = [
            lang.code for lang in self.secondary_languages.iterator()
        ]
        result["profile"]["watched"] = [
            project.slug for project in self.watched.iterator()
        ]
        return result

    @cached_property
    def primary_language_ids(self) -> Set[int]:
        return set(self.languages.values_list("pk", flat=True))

    @cached_property
    def allowed_dashboard_component_lists(self):
        return ComponentList.objects.filter(
            show_dashboard=True,
            components__project_id__in=self.user.allowed_project_ids,
        ).distinct()

    @cached_property
    def secondary_language_ids(self) -> Set[int]:
        return set(self.secondary_languages.values_list("pk", flat=True))

    def get_translation_order(self, translation) -> int:
        """Returns key suitable for ordering languages based on user preferences."""
        language = translation.language
        if language.pk in self.primary_language_ids:
            return 0
        if language.pk in self.secondary_language_ids:
            return 1
        if translation.is_source:
            return 2
        return 3

    @cached_property
    def watched_project_ids(self):
        # We do not use values_list, because we prefetch this
        return {watched.id for watched in self.watched.all()}

    def watches_project(self, project):
        return project.id in self.watched_project_ids
Esempio n. 7
0
class MateriaPrima(models.Model):
	id=models.AutoField(primary_key=True)
	nombreMateriaPrima= models.CharField(max_length = 256)
	cantidad = models.IntegerField()
	precioUnitario= models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 8
0
class Shift(models.Model):
    """ A shift of one job.

    Columns:
        :job: job of this shift
        :begin: begin of the shift
        :end: end of the shift
        :number: number of people
        :blocked: shift is blocked, if the job is public
        :hidden: shift is not displayed publicly
        :name: name of the shift (optional)
    """
    class Meta:
        ordering = ['job', 'begin', 'end']

    job = models.ForeignKey(
        'Job',
        on_delete=models.CASCADE,
    )

    name = models.CharField(
        max_length=200,
        verbose_name=_("Name (optional)"),
        default="",
        blank=True,
    )

    begin = models.DateTimeField(verbose_name=_("Begin"), )

    end = models.DateTimeField(verbose_name=_("End"), )

    number = models.IntegerField(
        default=0,
        verbose_name=_("Number of helpers"),
        validators=[MinValueValidator(0)],
    )

    blocked = models.BooleanField(
        default=False,
        verbose_name=_("The shift is blocked and displayed as full."),
    )

    hidden = models.BooleanField(
        default=False,
        verbose_name=_("The shift is not visible."),
    )

    gifts = models.ManyToManyField(
        'gifts.GiftSet',
        verbose_name=_("Gifts"),
        blank=True,
    )

    archived_number = models.IntegerField(
        default=0,
        verbose_name=_("Number of registered helpers for archived event"),
    )

    def __str__(self):
        if self.name:
            return "%s, %s, %s" % (self.job.name, self.name,
                                   self.time_with_day())
        else:
            return "%s, %s" % (self.job.name, self.time_with_day())

    def time(self):
        """ Returns a string representation of the begin and end time.

        The begin contains the date and time, the end only the time.
        """
        return "%s, %s - %s" % (date_f(localtime(self.begin), 'DATE_FORMAT'),
                                date_f(localtime(self.begin), 'TIME_FORMAT'),
                                date_f(localtime(self.end), 'TIME_FORMAT'))

    def time_hours(self):
        """ Returns a string representation of the begin and end time.

        Only the time is used, the date is not shown.
        """
        return "%s - %s" % (date_f(localtime(self.begin), 'TIME_FORMAT'),
                            date_f(localtime(self.end), 'TIME_FORMAT'))

    def time_with_day(self):
        """ Returns a string representation of the day.

        If the shift is on two days only the name of the first day is returned.
        """
        day = date_f(localtime(self.begin), "l")
        return "{}, {}".format(day, self.time())

    def date(self):
        """ Returns the day on which the shifts begins. """
        return localtime(self.begin).date()

    def num_helpers(self):
        """
        Returns the current number of helpers, but 0 if event is archived.
        """
        return self.helper_set.count()

    def num_helpers_archived(self):
        """ Returns the current number of helpers- """
        if self.job.event.archived:
            return self.archived_number
        else:
            return self.helper_set.count()

    def is_full(self):
        """ Check if the shift is full and return a boolean. """
        return self.num_helpers() >= self.number

    def helpers_percent(self):
        """ Calculate the percentage of registered helpers and returns an int.

        If the maximal number of helpers for a shift is 0, 0 is returned.
        """
        if self.number == 0:
            return 0

        num = self.num_helpers_archived()
        return int(round(float(num) / self.number * 100.0, 0))

    def helpers_percent_5percent(self):
        percent = self.helpers_percent()
        return math.ceil(percent / 5)

    @property
    def shirt_sizes(self):
        # data structure
        shirts = OrderedDict()
        for size, name in self.job.event.get_shirt_choices():
            shirts.update({name: 0})

        # collect all sizes, this must be the first shift of the helper
        for helper in self.helper_set.all():
            if helper.first_shift == self:
                tmp = shirts[helper.get_shirt_display()]
                shirts.update({helper.get_shirt_display(): tmp + 1})

        return shirts

    def duplicate(self, new_date=None, new_job=None, gift_set_mapping=None):
        """ Duplicate a shift. There are multiple possibilities:

        * Shift is copied to new day in same job: set new_date
        * Shift is copied to new job in same event: set new_job
        * Shift is copied to new event: set new_job and gift_set_mapping
        """
        new_shift = deepcopy(self)
        new_shift.pk = None
        new_shift.archived_number = 0

        # maybe shift is copied to new job
        if new_job:
            new_shift.job = new_job

            # if shift is copied to new event, move begin and end time according to diff in event dates
            if self.job.event != new_job.event:
                diff = new_job.event.date - self.job.event.date
                new_shift.begin += diff
                new_shift.end += diff

        # maybe just the date is changed
        if new_date:
            new_shift.begin = new_shift.begin.replace(year=new_date.year)
            new_shift.begin = new_shift.begin.replace(month=new_date.month)
            new_shift.begin = new_shift.begin.replace(day=new_date.day)

            new_shift.end = new_shift.end.replace(year=new_date.year)
            new_shift.end = new_shift.end.replace(month=new_date.month)
            new_shift.end = new_shift.end.replace(day=new_date.day)

        # now save that
        new_shift.save()

        # and finally set the gifts again
        for gift in self.gifts.all():
            if gift_set_mapping:
                new_shift.gifts.add(gift_set_mapping[gift])
            else:
                new_shift.gifts.add(gift)

        return new_shift
Esempio n. 9
0
class OrderLine(models.Model):
    order = models.ForeignKey(Order,
                              related_name="lines",
                              editable=False,
                              on_delete=models.CASCADE)
    variant = models.ForeignKey(
        "product.ProductVariant",
        related_name="order_lines",
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )
    # max_length is as produced by ProductVariant's display_product method
    product_name = models.CharField(max_length=386)
    variant_name = models.CharField(max_length=255, default="", blank=True)
    translated_product_name = models.CharField(max_length=386,
                                               default="",
                                               blank=True)
    translated_variant_name = models.CharField(max_length=255,
                                               default="",
                                               blank=True)
    product_sku = models.CharField(max_length=255)
    is_shipping_required = models.BooleanField()
    quantity = models.IntegerField(validators=[MinValueValidator(1)])
    quantity_fulfilled = models.IntegerField(validators=[MinValueValidator(0)],
                                             default=0)

    currency = models.CharField(
        max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH, )

    unit_discount_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
        default=0,
    )
    unit_discount = MoneyField(amount_field="unit_discount_amount",
                               currency_field="currency")
    unit_discount_type = models.CharField(
        max_length=10,
        choices=DiscountValueType.CHOICES,
        default=DiscountValueType.FIXED,
    )
    unit_discount_reason = models.TextField(blank=True, null=True)

    unit_price_net_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    # stores the value of the applied discount. Like 20 of %
    unit_discount_value = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
        default=0,
    )
    unit_price_net = MoneyField(amount_field="unit_price_net_amount",
                                currency_field="currency")

    unit_price_gross_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    unit_price_gross = MoneyField(amount_field="unit_price_gross_amount",
                                  currency_field="currency")

    unit_price = TaxedMoneyField(
        net_amount_field="unit_price_net_amount",
        gross_amount_field="unit_price_gross_amount",
        currency="currency",
    )

    total_price_net_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    total_price_net = MoneyField(
        amount_field="total_price_net_amount",
        currency_field="currency",
    )

    total_price_gross_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    total_price_gross = MoneyField(
        amount_field="total_price_gross_amount",
        currency_field="currency",
    )

    total_price = TaxedMoneyField(
        net_amount_field="total_price_net_amount",
        gross_amount_field="total_price_gross_amount",
        currency="currency",
    )

    tax_rate = models.DecimalField(max_digits=5,
                                   decimal_places=4,
                                   default=Decimal("0.0"))

    objects = OrderLineQueryset.as_manager()

    class Meta:
        ordering = ("pk", )

    def __str__(self):
        return (f"{self.product_name} ({self.variant_name})"
                if self.variant_name else self.product_name)

    @property
    def undiscounted_unit_price(self) -> "TaxedMoney":
        return self.unit_price + self.unit_discount

    @property
    def quantity_unfulfilled(self):
        return self.quantity - self.quantity_fulfilled

    @property
    def is_digital(self) -> Optional[bool]:
        """Check if a variant is digital and contains digital content."""
        if not self.variant:
            return None
        is_digital = self.variant.is_digital()
        has_digital = hasattr(self.variant, "digital_content")
        return is_digital and has_digital
Esempio n. 10
0
class FileInputForm(forms.Form, BaseFormFieldPluginForm):
    """Form for ``FileInputPlugin``."""

    plugin_data_fields = [
        ("label", ""),
        ("name", ""),
        ("help_text", ""),
        ("initial", ""),
        ("max_length", str(DEFAULT_MAX_LENGTH)),
        ("required", False)
    ]

    label = forms.CharField(
        label=_("Label"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={'class': theme.form_element_html_class}
        )
    )
    name = forms.CharField(
        label=_("Name"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={'class': theme.form_element_html_class}
        )
    )
    help_text = forms.CharField(
        label=_("Help text"),
        required=False,
        widget=forms.widgets.Textarea(
            attrs={'class': theme.form_element_html_class}
        )
    )
    initial = forms.CharField(
        label=_("Initial"),
        required=False,
        widget=forms.widgets.TextInput(
            attrs={'class': theme.form_element_html_class}
        )
    )
    max_length = forms.IntegerField(
        label=_("Max length"),
        required=True,
        widget=NumberInput(attrs={'class': theme.form_element_html_class,
                                  'min': str(DEFAULT_MIN_LENGTH)}),
        initial=DEFAULT_MAX_LENGTH,
        validators=[MinValueValidator(DEFAULT_MIN_LENGTH)]
    )
    required = forms.BooleanField(
        label=_("Required"),
        required=False,
        widget=forms.widgets.CheckboxInput(
            attrs={'class': theme.form_element_checkbox_html_class}
        )
    )

    def clean(self):
        super(FileInputForm, self).clean()

        max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH)

        if self.cleaned_data['initial']:
            len_initial = len(self.cleaned_data['initial'])
            if len_initial > max_length:
                self.add_error(
                    'initial',
                    _("Ensure this value has at most {0} characters "
                      "(it has {1}).".format(max_length, len_initial))
                )
Esempio n. 11
0
class game_settings(models.Model):
    """
    Game's basic settings.
    NOTE: Only uses the first record!
    """

    # The name of your game.
    game_name = models.CharField(max_length=NAME_LENGTH, blank=True)

    # The screen shows to players who are not loggin.
    connection_screen = models.TextField(blank=True)

    # In solo mode, a player can not see or affect other players.
    solo_mode = models.BooleanField(blank=True, default=False)

    # Time of global CD.
    global_cd = models.FloatField(blank=True,
                                  default=1.0,
                                  validators=[MinValueValidator(0.0)])

    # The CD of auto casting a skill. It must be bigger than GLOBAL_CD
    # They can not be equal!
    auto_cast_skill_cd = models.FloatField(blank=True,
                                           default=1.5,
                                           validators=[MinValueValidator(0.0)])

    # Allow players to give up quests.
    can_give_up_quests = models.BooleanField(blank=True, default=True)

    # can close dialogue box or not.
    can_close_dialogue = models.BooleanField(blank=True, default=False)

    # Can resume unfinished dialogues automatically.
    auto_resume_dialogues = models.BooleanField(blank=True, default=True)

    # The key of a world room.
    # It is the default home location used for all objects. This is used as a
    # fallback if an object's normal home location is deleted. It is the
    # key of the room. If it is empty, the home will be set to the first
    # room in WORLD_ROOMS.
    default_home_key = models.CharField(max_length=KEY_LENGTH, blank=True)

    # The key of a world room.
    # The start position for new characters. It is the key of the room.
    # If it is empty, the home will be set to the first room in WORLD_ROOMS.
    start_location_key = models.CharField(max_length=KEY_LENGTH, blank=True)

    # The key of a world room.
    # Player's default home. When a player dies, he will be moved to his home.
    default_player_home_key = models.CharField(max_length=KEY_LENGTH,
                                               blank=True)

    # The key of a character.
    # Default character of players.
    default_player_character_key = models.CharField(max_length=KEY_LENGTH,
                                                    blank=True)

    class Meta:
        "Define Django meta options"
        abstract = True
        app_label = "worlddata"
        verbose_name = "Game Setting"
        verbose_name_plural = "Game Settings"
Esempio n. 12
0
class DecimalFieldModel(models.Model):
    decimal_field = models.DecimalField(
        max_digits=3,
        decimal_places=1,
        validators=[MinValueValidator(1),
                    MaxValueValidator(3)])
Esempio n. 13
0
class Kheladi(models.Model):
    ROLE_CHOICES = (
        ('all rounder', 'all rounder'),
        ('bowler', 'bowler'),
        ('batsman', 'batsman'),
    )
    BATTING_CHOICES = (
        ('right hand bat', 'right hand bat'),
        ('left hand bat', 'left hand bat'),
    )
    BOWLING_CHOICES = (
        ('right arm fast', 'right arm fast'),
        ('left arm fast', 'left arm fast'),
        ('left arm spin', 'left arm spin'),
        ('right arm spin', 'right arm spin'),
    )
    first_name = models.CharField(max_length=50, db_index=True)
    last_name = models.CharField(max_length=50, )
    bio = models.TextField(db_index=True)
    favourite_cricketer = models.CharField(max_length=30, db_index=True)
    dp = CloudinaryField(verbose_name='profile picture', db_index=True)
    country = models.CharField(max_length=7, db_index=True)
    slug = models.SlugField(blank=True,
                            null=True,
                            editable=False,
                            db_index=True)
    uuid = models.UUIDField(blank=True,
                            null=True,
                            default=uuid.uuid4,
                            db_index=True)
    dob = models.DateField(verbose_name='Date of Birth',
                           default=timezone.now,
                           help_text='Enter in format YYYY-MM-DD',
                           db_index=True)
    age = models.PositiveIntegerField(blank=True, db_index=True)
    height = models.CharField(default=0, max_length=10, db_index=True)
    role = models.CharField(choices=ROLE_CHOICES,
                            max_length=20,
                            default='all rounder',
                            db_index=True)
    contact = models.CharField(max_length=30,
                               blank=True,
                               null=True,
                               db_index=True)
    batting_style = models.CharField(choices=BATTING_CHOICES,
                                     max_length=20,
                                     default='right hand bat',
                                     db_index=True)
    bowling_style = models.CharField(choices=BOWLING_CHOICES,
                                     max_length=20,
                                     default='right arm bowl',
                                     db_index=True)
    highest_score = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0),
                    MaxValueValidator(200)],
        db_index=True)
    highest_wicket = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0),
                    MaxValueValidator(10)],
        db_index=True)

    def __str__(self):
        return self.first_name

    def save(self, **kwargs):
        if not self.age:
            self.age = int((timezone.now().date() - self.dob).days / 365)
        if not self.slug:
            self.slug = slugify(f'{self.first_name} {self.last_name}')
        super(Kheladi, self).save(**kwargs)

    def get_absolute_url(self):
        return reverse('cricket:player_detail',
                       kwargs={
                           'slug': self.slug,
                           'uuid': self.uuid
                       })

    class Meta:
        verbose_name_plural = 'Kheladi'
        ordering = ['first_name']
Esempio n. 14
0
class Profile(models.Model):
    userid = models.CharField(primary_key=True,
                              unique=True,
                              max_length=6,
                              default='')
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    about = models.CharField(max_length=200)
    can_see = models.BooleanField(default=False)
    can_rate = models.BooleanField(default=True)
    updated_at = models.DateTimeField(auto_now=True)

    current_rating = models.FloatField(
        default=0.0,
        validators=[MinValueValidator(0.0),
                    MaxValueValidator(10.0)])
    cumulated_rating = models.FloatField(
        default=0.0,
        validators=[MinValueValidator(0.0),
                    MaxValueValidator(10.0)])

    public_key = models.TextField()  #user public key

    # ImageField¶ While adding profile pictures
    # class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)

    def __str__(self):
        return self.userid

    def updateMyRating(self, new_rating, session):
        # A control object must be present
        recent_control = (Control.objects.latest('updated_at'))
        recent_session_number = recent_control.session_number

        total_ratings_taken = Rating.objects.all().filter(
            user2=self.userid).filter(
                session_number=recent_session_number).count()
        recent_ratings_taken = Rating.objects.all().filter(
            user2=self.userid).filter(
                session_number=recent_session_number).count()

        total_ratings_taken = max(total_ratings_taken, 1)
        recent_ratings_taken = max(recent_ratings_taken, 1)

        #  if Divide by zero because of no ratings
        self.current_rating = (self.current_rating *
                               (recent_ratings_taken - 1) +
                               int(new_rating)) / recent_ratings_taken
        self.cumulated_rating = (self.cumulated_rating *
                                 (total_ratings_taken - 1) +
                                 int(new_rating)) / total_ratings_taken

        self.save()

    def update_can_see(self, session):
        recent_control = (Control.objects.latest('updated_at'))
        recent_session_number = recent_control.session_number
        threshold = recent_control.threshold_persons

        recent_ratings_given = Rating.objects.all().filter(
            user1=self.userid).filter(
                session_number=recent_session_number).count()

        self.can_see = True if recent_ratings_given > threshold else False
        self.save()

    def get_absolute_url(self):
        return ("/user/" + self.userid)

    def get_latest_work(self):
        works = Work.objects.filter(
            user=self).order_by('-updated_at').values('work')
        trueworks = []

        for work in works:
            data = work.get('work')
            trueworks.append(data)
        works = trueworks

        try:
            latest_work = works[0]
            decrypted_work = signing.loads(latest_work)
            return decrypted_work[0]
        except:
            return None

    @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance, userid=instance.username)

    @receiver(post_save, sender=User)
    def save_user_profile(sender, instance, **kwargs):
        instance.profile.save()
Esempio n. 15
0
class Device(LoggingModel):
    """
    Device instance with. Each device must be assigned to location and can be place to rack.
    """

    #TODO: добавить обьединение в кластер, генерация IP адресов

    FRONT = 1
    REAR = 0

    RACK_SIDE_CHOICES = (
        (FRONT, 'Located at the front of rack'),
        (REAR, 'Located at the rear of rack'),
    )

    name = models.CharField(
        max_length=50,
        unique=True
    )
    device_model = models.ForeignKey(
        to=VendorModel,
        on_delete=models.PROTECT,
        related_name='instances'
    )
    device_role = models.ForeignKey(
        to=DeviceRole,
        on_delete=models.PROTECT,
        related_name='devices'
    )
    platform = models.ForeignKey(
        to=Platform,
        on_delete=models.SET_NULL,
        related_name='devices',
        blank=True,
        null=True
    )
    serial = models.CharField(
        max_length=50,
        blank=True,
        verbose_name='Serial number'
    )
    location = models.ForeignKey(
        to=Location,
        on_delete=models.PROTECT,
        related_name='devices'
    )
    rack = models.ForeignKey(
        to=Rack,
        on_delete=models.PROTECT,
        related_name='devices',
        blank=True,
        null=True
    )
    position = models.PositiveSmallIntegerField(
        blank=True,
        null=True,
        validators=[MinValueValidator(1)],
        verbose_name='Unit Number',
        help_text='Unit Number device starts location in the rack'
    )
    face_position = models.PositiveSmallIntegerField(
        choices=RACK_SIDE_CHOICES,
        default=FRONT,
        blank=True,
        null=True
    )
#    cluster = models.ForeignKey(
#        to='Cluster',
#        on_delete=models.SET_NULL,
#        related_name='devices',
#        blank=True,
#        null=True
#    )
#    primary_ip = models.OneToOneField(
#        to='IPAddress',
#        on_delete=models.SET_NULL,
#        related_name='primary_ip',
#        blank=True,
#        null=True,
#        verbose_name='Основной IP адрес'
#    )
    description = models.CharField(
        max_length=100,
        blank=True
    )
    comment = models.TextField(
        blank=True
    )
    images = GenericRelation(
        to=ImgAttach
    )
    tag = TaggableManager(
        through=TaggedItem
    )

    class Meta:
        ordering = ('name', 'pk')

    def __str__(self):
        return self.display_name or super().__str__()
        return self.name

    @property
    def display_name(self):
        if self.name:
            return self.name
        elif hasattr(self, 'device_model'):
            return "{}".format(self.device_model)
        return ""

    def get_absolute_url(self):
        return reverse('organisation:device', args=[self.pk])

    def clean(self):

        super().clean()

        if self.rack is None:
            if self.position:
                raise ValidationError({
                    'position': "Cannot select position without rack selected.",
                })
            if self.face_position:
                raise ValidationError({
                    'face_position': "Cannot select face_position without rack selected.",
                })
Esempio n. 16
0
class Analysis(models.Model):
    name = models.CharField(max_length=30, unique=True, validators=[MinLengthValidator(3)])
    date_create = models.DateField(auto_now_add=True)
    date_modification = models.DateField(auto_now=True)
    data_set = models.FileField(upload_to="data_sets/")
    signal_speed = models.IntegerField(validators=[MaxValueValidator(4), MinValueValidator(1)])
    signal_direction = models.IntegerField(validators=[MaxValueValidator(4), MinValueValidator(1)])
    step_group = models.IntegerField(validators=[greater_zero])
    start_sector_direction = models.IntegerField(
        validators=[MaxValueValidator(360), MinValueValidator(0)]
    )
    end_sector_direction = models.IntegerField(
        validators=[MaxValueValidator(360), MinValueValidator(0)]
    )
    start_sector_speed = models.IntegerField(validators=[MinValueValidator(0)])
    end_sector_speed = models.IntegerField(validators=[MinValueValidator(0)])
    result_analysis = models.FilePathField(blank=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["date_modification"]

    def delete(self, using=None, keep_parents=False):
        shutil.rmtree(self.result_analysis)
        self.data_set.delete(save=False)
        super(Analysis, self).delete(using, keep_parents)

    def delete_results_analysis(self):
        if os.path.exists(self.result_analysis):
            for analysis_file in os.listdir(self.result_analysis):
                os.remove(os.path.join(self.result_analysis, analysis_file))
        logging.warning(f"Path does not exists {self.result_analysis}")

    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
        is_new = self.pk is None
        self.calculate_analysis()
        if is_new:
            self.result_analysis = os.path.join(settings.MEDIA_ROOT, self.name)
        super().save(force_insert, force_update, using, update_fields)

    def calculate_analysis(self):
        try:
            ApplicationManager().go(
                self,
                settings.MEDIA_ROOT,
            )
        except Exception as e:
            try:
                Analysis.objects.get(name=self.name)
            except ObjectDoesNotExist:
                shutil.rmtree(os.path.join(settings.MEDIA_ROOT, self.name))
                raise ValidationError(e)

    def create_archive(self):
        file_name = "{}_{}.zip".format(self.name, self.date_modification)
        base_name_dir = "zip_files"
        abs_path_dir = os.path.join(settings.MEDIA_ROOT, base_name_dir)
        if not os.path.exists(abs_path_dir):
            os.mkdir(abs_path_dir)
        compress_zip(os.path.join(abs_path_dir, file_name), get_all_abs_paths(self.result_analysis))
        archive = ZipArchive()
        archive.name = self.name
        archive.zip_file = os.path.join(base_name_dir, file_name)
        archive.analysis = self
        archive.save()
        return archive

    def delete_archive(self):
        try:
            ZipArchive.objects.get(name=self.name).delete()
        except Exception:
            pass
Esempio n. 17
0
class Rack(LoggingModel):
    """
    Rack configuration
    """
    TYPE_1FRAME = '1-frame'
    TYPE_2FRAME = '2-frame'
    TYPE_WALLCABINET = 'wall-cabinet'
    TYPE_FLOORCABINET = 'floor-cabinet'

    TOP_TO_BUTTOM = 0
    BUTTOM_TO_TOP = 1


    RACK_TYPE_CHOICES = (
        (TYPE_1FRAME, 'One Frame rack'),
        (TYPE_2FRAME, 'Two Frame rack'),
        (TYPE_WALLCABINET, 'Wall-mounted cabinet'),
        (TYPE_FLOORCABINET, 'Floor-mounted cabinet'),
    )
    RACK_UNIT_DESC = (
        (TOP_TO_BUTTOM, 'Top to buttom'),
        (BUTTOM_TO_TOP, 'Buttom to top'),
    )
    name = models.CharField(
        max_length=50
    )
    location = models.ForeignKey(
        to=Location,
        on_delete=models.PROTECT,
        related_name='racks'
    )
    u_height = models.PositiveSmallIntegerField(
        default=44,
        verbose_name='Unit Height',
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    desc_units = models.BooleanField(
        choices=RACK_UNIT_DESC,
        default=BUTTOM_TO_TOP,
        verbose_name='Orientation',
        help_text='Default buttom to top'
    )
    racktype = models.CharField(
        max_length=50,
        choices=RACK_TYPE_CHOICES,
        default=TYPE_FLOORCABINET,
    )
    comment = models.TextField(
        blank=True
    )

    class Meta:
        ordering = ('location', 'name', 'pk')

    def __str__(self):
        return self.name

    # def __str__(self):
    #     return self.display_name or super().__str__()

    def get_absolute_url(self):
        return reverse('organisation:rack', args=[self.pk])
Esempio n. 18
0
class Organizacion(models.Model):
    """ Tabla de las organizaciones que proponen un proyecto
    al EsIA.
    Parametros:
        models.Model (Organizacion): Instancia sobre la que se
        crea la tabla.
    Atributos de la clase:
        proyecto: proyecto al que pertenece la organizacion
        razon_social: Razon social de la organizacion
        nombre: Nombre de la orgnizacion
        rif: Rif de la organizacion
        direccion: direccion de la organizacion
        representante_legal: Datos del representante legal de la organizacion
        telefono: Telefono de la organizacion
        email: Email de la organizacion
    """
    proyecto = models.ForeignKey(DatosProyecto, on_delete=models.CASCADE)
    RAZON_SOCIAL_CHOICES = (('natural', 'Persona natural'),
                            ('juridica', 'Persona Jurídica'))
    razon_social = models.CharField(max_length=8, choices=RAZON_SOCIAL_CHOICES)
    nombre = models.CharField(max_length=100)
    rif = models.CharField(
        max_length=12,
        unique=True,
        validators=[
            RegexValidator(
                re.compile('^([VEJPGvejpg]{1})-([0-9]{8})-([0-9]{1}$)'),
                _('RIF incorrecto'), 'invalid')
        ])
    direccion = models.TextField()
    nombre_representante_legal = models.CharField(
        max_length=60,
        validators=[
            RegexValidator(re.compile(r'^[\w+\s]+$'), _('Nombre incorrecto'),
                           'invalid')
        ])
    apellido_representante_legal = models.CharField(
        max_length=60,
        validators=[
            RegexValidator(re.compile(r'^[\w+\s]+$'), _('Apellido incorrecto'),
                           'invalid')
        ])
    cedula_representante_legal = models.CharField(
        max_length=9,
        validators=[
            RegexValidator(re.compile('^[V|E|J|P][0-9]{5,9}$'),
                           _('Cédula incorrecta'), 'invalid')
        ])
    pasaporte_representante_legal = models.IntegerField(
        validators=[MinValueValidator(0)], blank=True, null=True)
    telefono = models.CharField(max_length=11,
                                validators=[
                                    RegexValidator((re.compile('^[0-9]{11}$')),
                                                   _('Teléfono incorrecto'),
                                                   'invalid')
                                ])
    email = models.EmailField()

    def get_model_type(self):  # pylint: disable=no-self-use
        '''Devuelve el tipo de modelo'''
        return "Organizacion"
Esempio n. 19
0
from django.db.models import *
import uuid
from django.core.validators import MinValueValidator, MaxValueValidator, URLValidator, ValidationError
from common.utils import all_h_tags
import bleach
import re

zero_to_one_validator = [
    MinValueValidator(0, "This can't be a negative value"),
    MaxValueValidator(1, "This can't be greater than 1")
]



ALLOWED_HTML_TAGS = [
    *bleach.sanitizer.ALLOWED_TAGS,
    *['p', 'dl', 'dd', 'dt', 'img', 
      'svg', 'span', 'div', 's', 'hr',
      'sub', 'sup', 'code', 'table', 
      'tr', 'td', 'th', 'tbody', 'thead',
      'pre', 'br', 'u'],
    *all_h_tags()
]

ALLOWED_HTML_ATTRS = {
    **bleach.sanitizer.ALLOWED_ATTRIBUTES,
    **{
        'img': ['src', 'alt'],
        'svg': ['src', 'alt'],
        'span': ['style', 'class'],
        'div': ['style', 'class']
Esempio n. 20
0
class Denizen(models.Model):
    name = models.CharField(_("name"), max_length=30)
    fathers_name = models.CharField(_("father's name"),
                                    max_length=20,
                                    null=True,
                                    blank=True)

    phone = models.CharField(_("phone"), max_length=30)
    edu_foundations = models.CharField(_("Education Foundations"),
                                       max_length=60,
                                       default='',
                                       blank=True)
    email = models.EmailField(_("email"), null=True, blank=True)
    state = models.CharField(_("state"),
                             blank=True,
                             max_length=1,
                             null=True,
                             choices=STATE_CHOICES,
                             default='N')
    hood = models.CharField(_("Neighborhood"),
                            blank=True,
                            max_length=32,
                            choices=HOOD_CHOICES)
    election_day = models.CharField(_("willing to voounteer on election day"),
                                    blank=True,
                                    max_length=1,
                                    choices=VOLUNTEER_CHOICES)
    second_ring = models.CharField(_("willing to be in the second ring"),
                                   blank=True,
                                   max_length=1,
                                   choices=VOLUNTEER_CHOICES)
    home_meetup = models.CharField(_("willing to host a meetup"),
                                   blank=True,
                                   max_length=1,
                                   choices=VOLUNTEER_CHOICES)
    palrig = models.CharField(_("willing to hang a palrig"),
                              blank=True,
                              max_length=1,
                              choices=VOLUNTEER_CHOICES)
    call_center = models.CharField(_("willing to be a call center volounteer"),
                                   blank=True,
                                   max_length=1,
                                   choices=VOLUNTEER_CHOICES)
    field = models.CharField(_("willing to be a volounteer in the field"),
                             blank=True,
                             max_length=1,
                             choices=VOLUNTEER_CHOICES)
    digital = models.CharField(_("willing to  digital volounteer"),
                               blank=True,
                               max_length=1,
                               choices=VOLUNTEER_CHOICES)
    meetup_guest = models.CharField(_("want to be a guest in a meetup"),
                                    blank=True,
                                    max_length=1,
                                    choices=VOLUNTEER_CHOICES)
    women_rights = models.BooleanField(_("Interested in women rights"),
                                       default=False)
    participation = models.BooleanField(_("Interested in civic participation"),
                                        default=False)
    commons = models.BooleanField(_("Interested in the commons"),
                                  default=False)
    education = models.BooleanField(_("Interested in education"),
                                    default=False)
    religion = models.BooleanField(_("Interested in religion"), default=False)
    address = models.CharField(_("Address"),
                               max_length=40,
                               blank=True,
                               default='')
    notes = models.TextField(_("Notes"), blank=True, default='')
    zehut = models.CharField(_("zehut"), max_length=9, blank=True, null=True)
    birth_year = models.IntegerField(
        _("birth year"),
        blank=True,
        null=True,
        validators=[
            MinValueValidator(1910,
                              _("they can't be more than 108 years old")),
            MaxValueValidator(2000, _("They can't be younger than 18"))
        ])
    ballot = models.CharField(_("ballot name"), blank=True, max_length=30)
    source = models.CharField(_("Source"), max_length=20)
    whatever = models.CharField(_("Whatever"), max_length=60, default='')
    created_on = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _("Denizen")
        verbose_name_plural = _("Denizens")

    def save(self):
        #TODO: DRY !!!
        if self.election_day.lower() == 'x':
            self.election_day = 'W'
        if self.second_ring.lower() == 'x':
            self.second_ring = 'W'
        if self.home_meetup.lower() == 'x':
            self.home_meetup = 'W'
        if self.palrig.lower() == 'x':
            self.palrig = 'W'
        if self.digital.lower() == 'x':
            self.digital = 'W'
        if self.election_day.lower() == 'x':
            self.election_day = 'W'
        if self.election_day.lower() == 'x':
            self.election_day = 'W'
        if self.field.lower() == 'x':
            self.field = 'W'
        if self.meetup_guest.lower() == 'x':
            self.meetup_guest = 'W'
        super().save()

    def __str__(self):
        return self.name
Esempio n. 21
0
class CIF(models.Model):
	id=models.AutoField(primary_key=True)
	porcentaje=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 22
0
class BomItem(models.Model):
    """ A BomItem links a part to its component items.
    A part can have a BOM (bill of materials) which defines
    which parts are required (and in what quatity) to make it.

    Attributes:
        part: Link to the parent part (the part that will be produced)
        sub_part: Link to the child part (the part that will be consumed)
        quantity: Number of 'sub_parts' consumed to produce one 'part'
        reference: BOM reference field (e.g. part designators)
        overage: Estimated losses for a Build. Can be expressed as absolute value (e.g. '7') or a percentage (e.g. '2%')
        note: Note field for this BOM item
        checksum: Validation checksum for the particular BOM line item
    """
    def get_absolute_url(self):
        return reverse('bom-item-detail', kwargs={'pk': self.id})

    # A link to the parent part
    # Each part will get a reverse lookup field 'bom_items'
    part = models.ForeignKey(Part,
                             on_delete=models.CASCADE,
                             related_name='bom_items',
                             help_text=_('Select parent part'),
                             limit_choices_to={
                                 'assembly': True,
                             })

    # A link to the child item (sub-part)
    # Each part will get a reverse lookup field 'used_in'
    sub_part = models.ForeignKey(Part,
                                 on_delete=models.CASCADE,
                                 related_name='used_in',
                                 help_text=_('Select part to be used in BOM'),
                                 limit_choices_to={
                                     'component': True,
                                 })

    # Quantity required
    quantity = models.DecimalField(
        default=1.0,
        max_digits=15,
        decimal_places=5,
        validators=[MinValueValidator(0)],
        help_text=_('BOM quantity for this BOM item'))

    overage = models.CharField(
        max_length=24,
        blank=True,
        validators=[validators.validate_overage],
        help_text=_(
            'Estimated build wastage quantity (absolute or percentage)'))

    reference = models.CharField(max_length=500,
                                 blank=True,
                                 help_text=_('BOM item reference'))

    # Note attached to this BOM line item
    note = models.CharField(max_length=500,
                            blank=True,
                            help_text=_('BOM item notes'))

    checksum = models.CharField(max_length=128,
                                blank=True,
                                help_text=_('BOM line checksum'))

    def get_item_hash(self):
        """ Calculate the checksum hash of this BOM line item:

        The hash is calculated from the following fields:

        - Part.full_name (if the part name changes, the BOM checksum is invalidated)
        - Quantity
        - Reference field
        - Note field

        """

        # Seed the hash with the ID of this BOM item
        hash = hashlib.md5(str(self.id).encode())

        # Update the hash based on line information
        hash.update(str(self.sub_part.id).encode())
        hash.update(str(self.sub_part.full_name).encode())
        hash.update(str(self.quantity).encode())
        hash.update(str(self.note).encode())
        hash.update(str(self.reference).encode())

        return str(hash.digest())

    def validate_hash(self, valid=True):
        """ Mark this item as 'valid' (store the checksum hash).
        
        Args:
            valid: If true, validate the hash, otherwise invalidate it (default = True)
        """

        if valid:
            self.checksum = str(self.get_item_hash())
        else:
            self.checksum = ''

        self.save()

    @property
    def is_line_valid(self):
        """ Check if this line item has been validated by the user """

        # Ensure an empty checksum returns False
        if len(self.checksum) == 0:
            return False

        return self.get_item_hash() == self.checksum

    def clean(self):
        """ Check validity of the BomItem model.

        Performs model checks beyond simple field validation.

        - A part cannot refer to itself in its BOM
        - A part cannot refer to a part which refers to it
        """

        # A part cannot refer to itself in its BOM
        try:
            if self.sub_part is not None and self.part is not None:
                if self.part == self.sub_part:
                    raise ValidationError({
                        'sub_part':
                        _('Part cannot be added to its own Bill of Materials')
                    })

            # TODO - Make sure that there is no recusion

            # Test for simple recursion
            for item in self.sub_part.bom_items.all():
                if self.part == item.sub_part:
                    raise ValidationError({
                        'sub_part':
                        _("Part '{p1}' is  used in BOM for '{p2}' (recursive)".
                          format(p1=str(self.part), p2=str(self.sub_part)))
                    })

        except Part.DoesNotExist:
            # A blank Part will be caught elsewhere
            pass

    class Meta:
        verbose_name = "BOM Item"

        # Prevent duplication of parent/child rows
        unique_together = ('part', 'sub_part')

    def __str__(self):
        return "{n} x {child} to make {parent}".format(
            parent=self.part.full_name,
            child=self.sub_part.full_name,
            n=helpers.decimal2string(self.quantity))

    def get_overage_quantity(self, quantity):
        """ Calculate overage quantity
        """

        # Most of the time overage string will be empty
        if len(self.overage) == 0:
            return 0

        overage = str(self.overage).strip()

        # Is the overage an integer value?
        try:
            ovg = int(overage)

            if ovg < 0:
                ovg = 0

            return ovg
        except ValueError:
            pass

        # Is the overage a percentage?
        if overage.endswith('%'):
            overage = overage[:-1].strip()

            try:
                percent = float(overage) / 100.0
                if percent > 1:
                    percent = 1
                if percent < 0:
                    percent = 0

                return int(percent * quantity)

            except ValueError:
                pass

        # Default = No overage
        return 0

    def get_required_quantity(self, build_quantity):
        """ Calculate the required part quantity, based on the supplier build_quantity.
        Includes overage estimate in the returned value.
        
        Args:
            build_quantity: Number of parts to build

        Returns:
            Quantity required for this build (including overage)
        """

        # Base quantity requirement
        base_quantity = self.quantity * build_quantity

        return base_quantity + self.get_overage_quantity(base_quantity)

    @property
    def price_range(self):
        """ Return the price-range for this BOM item. """

        prange = self.sub_part.get_price_range(self.quantity)

        if prange is None:
            return prange

        pmin, pmax = prange

        # remove trailing zeros
        pmin = pmin.normalize()
        pmax = pmax.normalize()

        if pmin == pmax:
            return str(pmin)

        return "{pmin} to {pmax}".format(pmin=pmin, pmax=pmax)
Esempio n. 23
0
class Final(models.Model):
	id=models.AutoField(primary_key=True)
	kardex=models.ForeignKey(Kardex, null=True, blank=True, on_delete=models.CASCADE)
	fechaFinal= models.DateField('Fecha de Final', help_text='Formato: AAAA/MM/DD', blank=False, null=False)
	cantidadFinal= models.IntegerField()
	costoTotalFinal= models.DecimalField('Costo Final', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	costoUnitarioFinal= models.DecimalField('Costo Unitario FInal', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	es_Actual= models.NullBooleanField(null = True, default=False);
Esempio n. 24
0
class Part(models.Model):
    """ The Part object represents an abstract part, the 'concept' of an actual entity.

    An actual physical instance of a Part is a StockItem which is treated separately.

    Parts can be used to create other parts (as part of a Bill of Materials or BOM).

    Attributes:
        name: Brief name for this part
        variant: Optional variant number for this part - Must be unique for the part name
        category: The PartCategory to which this part belongs
        description: Longer form description of the part
        keywords: Optional keywords for improving part search results
        IPN: Internal part number (optional)
        revision: Part revision
        is_template: If True, this part is a 'template' part and cannot be instantiated as a StockItem
        URL: Link to an external page with more information about this part (e.g. internal Wiki)
        image: Image of this part
        default_location: Where the item is normally stored (may be null)
        default_supplier: The default SupplierPart which should be used to procure and stock this part
        minimum_stock: Minimum preferred quantity to keep in stock
        units: Units of measure for this part (default='pcs')
        salable: Can this part be sold to customers?
        assembly: Can this part be build from other parts?
        component: Can this part be used to make other parts?
        purchaseable: Can this part be purchased from suppliers?
        trackable: Trackable parts can have unique serial numbers assigned, etc, etc
        active: Is this part active? Parts are deactivated instead of being deleted
        virtual: Is this part "virtual"? e.g. a software product or similar
        notes: Additional notes field for this part
    """
    class Meta:
        verbose_name = "Part"
        verbose_name_plural = "Parts"

    def __str__(self):
        return "{n} - {d}".format(n=self.full_name, d=self.description)

    @property
    def full_name(self):
        """ Format a 'full name' for this Part.

        - IPN (if not null)
        - Part name
        - Part variant (if not null)

        Elements are joined by the | character
        """

        elements = []

        if self.IPN:
            elements.append(self.IPN)

        elements.append(self.name)

        if self.revision:
            elements.append(self.revision)

        return ' | '.join(elements)

    def set_category(self, category):

        # Ignore if the category is already the same
        if self.category == category:
            return

        self.category = category
        self.save()

    def get_absolute_url(self):
        """ Return the web URL for viewing this part """
        return reverse('part-detail', kwargs={'pk': self.id})

    def get_image_url(self):
        """ Return the URL of the image for this part """

        if self.image:
            return os.path.join(settings.MEDIA_URL, str(self.image.url))
        else:
            return os.path.join(settings.STATIC_URL, 'img/blank_image.png')

    def validate_unique(self, exclude=None):
        """ Validate that a part is 'unique'.
        Uniqueness is checked across the following (case insensitive) fields:

        * Name
        * IPN
        * Revision

        e.g. there can exist multiple parts with the same name, but only if
        they have a different revision or internal part number.

        """
        super().validate_unique(exclude)

        # Part name uniqueness should be case insensitive
        try:
            parts = Part.objects.exclude(id=self.id).filter(
                name__iexact=self.name,
                IPN__iexact=self.IPN,
                revision__iexact=self.revision)

            if parts.exists():
                msg = _("Part must be unique for name, IPN and revision")
                raise ValidationError({
                    "name": msg,
                    "IPN": msg,
                    "revision": msg,
                })
        except Part.DoesNotExist:
            pass

    def clean(self):
        """ Perform cleaning operations for the Part model """

        if self.is_template and self.variant_of is not None:
            raise ValidationError({
                'is_template':
                _("Part cannot be a template part if it is a variant of another part"
                  ),
                'variant_of':
                _("Part cannot be a variant of another part if it is already a template"
                  ),
            })

    name = models.CharField(max_length=100,
                            blank=False,
                            help_text=_('Part name'),
                            validators=[validators.validate_part_name])

    is_template = models.BooleanField(
        default=False, help_text=_('Is this part a template part?'))

    variant_of = models.ForeignKey(
        'part.Part',
        related_name='variants',
        null=True,
        blank=True,
        limit_choices_to={
            'is_template': True,
            'active': True,
        },
        on_delete=models.SET_NULL,
        help_text=_('Is this part a variant of another part?'))

    description = models.CharField(max_length=250,
                                   blank=False,
                                   help_text=_('Part description'))

    keywords = models.CharField(
        max_length=250,
        blank=True,
        help_text=_('Part keywords to improve visibility in search results'))

    category = TreeForeignKey(PartCategory,
                              related_name='parts',
                              null=True,
                              blank=True,
                              on_delete=models.DO_NOTHING,
                              help_text=_('Part category'))

    IPN = models.CharField(max_length=100,
                           blank=True,
                           help_text=_('Internal Part Number'))

    revision = models.CharField(max_length=100,
                                blank=True,
                                help_text=_('Part revision or version number'))

    URL = InvenTreeURLField(blank=True, help_text=_('Link to extenal URL'))

    image = models.ImageField(upload_to=rename_part_image,
                              max_length=255,
                              null=True,
                              blank=True)

    default_location = TreeForeignKey(
        'stock.StockLocation',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        help_text=_('Where is this item normally stored?'),
        related_name='default_parts')

    def get_default_location(self):
        """ Get the default location for a Part (may be None).

        If the Part does not specify a default location,
        look at the Category this part is in.
        The PartCategory object may also specify a default stock location
        """

        if self.default_location:
            return self.default_location
        elif self.category:
            # Traverse up the category tree until we find a default location
            cats = self.category.get_ancestors(ascending=True,
                                               include_self=True)

            for cat in cats:
                if cat.default_location:
                    return cat.default_location

        # Default case - no default category found
        return None

    def get_default_supplier(self):
        """ Get the default supplier part for this part (may be None).

        - If the part specifies a default_supplier, return that
        - If there is only one supplier part available, return that
        - Else, return None
        """

        if self.default_supplier:
            return self.default_supplier

        if self.supplier_count == 1:
            return self.supplier_parts.first()

        # Default to None if there are multiple suppliers to choose from
        return None

    default_supplier = models.ForeignKey(SupplierPart,
                                         on_delete=models.SET_NULL,
                                         blank=True,
                                         null=True,
                                         help_text=_('Default supplier part'),
                                         related_name='default_parts')

    minimum_stock = models.PositiveIntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        help_text=_('Minimum allowed stock level'))

    units = models.CharField(max_length=20,
                             default="",
                             blank=True,
                             help_text=_('Stock keeping units for this part'))

    assembly = models.BooleanField(
        default=False,
        verbose_name='Assembly',
        help_text=_('Can this part be built from other parts?'))

    component = models.BooleanField(
        default=True,
        verbose_name='Component',
        help_text=_('Can this part be used to build other parts?'))

    trackable = models.BooleanField(
        default=False,
        help_text=_('Does this part have tracking for unique items?'))

    purchaseable = models.BooleanField(
        default=True,
        help_text=_('Can this part be purchased from external suppliers?'))

    salable = models.BooleanField(
        default=False, help_text=_("Can this part be sold to customers?"))

    active = models.BooleanField(default=True,
                                 help_text=_('Is this part active?'))

    virtual = models.BooleanField(
        default=False,
        help_text=_(
            'Is this a virtual part, such as a software product or license?'))

    notes = models.TextField(blank=True)

    bom_checksum = models.CharField(max_length=128,
                                    blank=True,
                                    help_text=_('Stored BOM checksum'))

    bom_checked_by = models.ForeignKey(User,
                                       on_delete=models.SET_NULL,
                                       blank=True,
                                       null=True,
                                       related_name='boms_checked')

    bom_checked_date = models.DateField(blank=True, null=True)

    def format_barcode(self):
        """ Return a JSON string for formatting a barcode for this Part object """

        return helpers.MakeBarcode(
            "Part", self.id, reverse('api-part-detail',
                                     kwargs={'pk': self.id}), {
                                         'name': self.name,
                                     })

    @property
    def category_path(self):
        if self.category:
            return self.category.pathstring
        return ''

    @property
    def available_stock(self):
        """
        Return the total available stock.

        - This subtracts stock which is already allocated to builds
        """

        total = self.total_stock

        total -= self.allocation_count

        return max(total, 0)

    @property
    def quantity_to_order(self):
        """ Return the quantity needing to be ordered for this part. """

        required = -1 * self.net_stock
        return max(required, 0)

    @property
    def net_stock(self):
        """ Return the 'net' stock. It takes into account:

        - Stock on hand (total_stock)
        - Stock on order (on_order)
        - Stock allocated (allocation_count)

        This number (unlike 'available_stock') can be negative.
        """

        return self.total_stock - self.allocation_count + self.on_order

    def isStarredBy(self, user):
        """ Return True if this part has been starred by a particular user """

        try:
            PartStar.objects.get(part=self, user=user)
            return True
        except PartStar.DoesNotExist:
            return False

    def need_to_restock(self):
        """ Return True if this part needs to be restocked
        (either by purchasing or building).

        If the allocated_stock exceeds the total_stock,
        then we need to restock.
        """

        return (self.total_stock + self.on_order -
                self.allocation_count) < self.minimum_stock

    @property
    def can_build(self):
        """ Return the number of units that can be build with available stock
        """

        # If this part does NOT have a BOM, result is simply the currently available stock
        if not self.has_bom:
            return 0

        total = None

        # Calculate the minimum number of parts that can be built using each sub-part
        for item in self.bom_items.all().prefetch_related(
                'sub_part__stock_items'):
            stock = item.sub_part.available_stock
            n = int(stock / item.quantity)

            if total is None or n < total:
                total = n

        return max(total, 0)

    @property
    def active_builds(self):
        """ Return a list of outstanding builds.
        Builds marked as 'complete' or 'cancelled' are ignored
        """

        return self.builds.filter(status__in=BuildStatus.ACTIVE_CODES)

    @property
    def inactive_builds(self):
        """ Return a list of inactive builds
        """

        return self.builds.exclude(status__in=BuildStatus.ACTIVE_CODES)

    @property
    def quantity_being_built(self):
        """ Return the current number of parts currently being built
        """

        return sum([b.quantity for b in self.active_builds])

    @property
    def build_allocation(self):
        """ Return list of builds to which this part is allocated
        """

        builds = []

        for item in self.used_in.all().prefetch_related('part__builds'):

            active = item.part.active_builds

            for build in active:
                b = {}

                b['build'] = build
                b['quantity'] = item.quantity * build.quantity

                builds.append(b)

        prefetch_related_objects(builds, 'build_items')

        return builds

    @property
    def allocated_build_count(self):
        """ Return the total number of this part that are allocated for builds
        """

        return sum([a['quantity'] for a in self.build_allocation])

    @property
    def allocation_count(self):
        """ Return true if any of this part is allocated:

        - To another build
        - To a customer order
        """

        return sum([
            self.allocated_build_count,
        ])

    @property
    def stock_entries(self):
        """ Return all 'in stock' items. To be in stock:

        - customer is None
        - belongs_to is None
        """

        return self.stock_items.filter(customer=None, belongs_to=None)

    @property
    def total_stock(self):
        """ Return the total stock quantity for this part.
        Part may be stored in multiple locations
        """

        if self.is_template:
            total = sum(
                [variant.total_stock for variant in self.variants.all()])
        else:
            total = self.stock_entries.filter(
                status__in=StockStatus.AVAILABLE_CODES).aggregate(
                    total=Sum('quantity'))['total']

        if total:
            return total
        else:
            return 0

    @property
    def has_bom(self):
        return self.bom_count > 0

    @property
    def bom_count(self):
        """ Return the number of items contained in the BOM for this part """
        return self.bom_items.count()

    @property
    def used_in_count(self):
        """ Return the number of part BOMs that this part appears in """
        return self.used_in.count()

    def get_bom_hash(self):
        """ Return a checksum hash for the BOM for this part.
        Used to determine if the BOM has changed (and needs to be signed off!)

        The hash is calculated by hashing each line item in the BOM.

        returns a string representation of a hash object which can be compared with a stored value
        """

        hash = hashlib.md5(str(self.id).encode())

        for item in self.bom_items.all().prefetch_related('sub_part'):
            hash.update(str(item.get_item_hash()).encode())

        return str(hash.digest())

    @property
    def is_bom_valid(self):
        """ Check if the BOM is 'valid' - if the calculated checksum matches the stored value
        """

        return self.get_bom_hash() == self.bom_checksum

    @transaction.atomic
    def validate_bom(self, user):
        """ Validate the BOM (mark the BOM as validated by the given User.

        - Calculates and stores the hash for the BOM
        - Saves the current date and the checking user
        """

        # Validate each line item too
        for item in self.bom_items.all():
            item.validate_hash()

        self.bom_checksum = self.get_bom_hash()
        self.bom_checked_by = user
        self.bom_checked_date = datetime.now().date()

        self.save()

    @transaction.atomic
    def clear_bom(self):
        """ Clear the BOM items for the part (delete all BOM lines).
        """

        self.bom_items.all().delete()

    def required_parts(self):
        """ Return a list of parts required to make this part (list of BOM items) """
        parts = []
        for bom in self.bom_items.all().select_related('sub_part'):
            parts.append(bom.sub_part)
        return parts

    def get_allowed_bom_items(self):
        """ Return a list of parts which can be added to a BOM for this part.

        - Exclude parts which are not 'component' parts
        - Exclude parts which this part is in the BOM for
        """

        parts = Part.objects.filter(component=True).exclude(id=self.id)
        parts = parts.exclude(id__in=[part.id for part in self.used_in.all()])

        return parts

    @property
    def supplier_count(self):
        """ Return the number of supplier parts available for this part """
        return self.supplier_parts.count()

    @property
    def has_pricing_info(self):
        """ Return true if there is pricing information for this part """
        return self.get_price_range() is not None

    @property
    def has_complete_bom_pricing(self):
        """ Return true if there is pricing information for each item in the BOM. """

        for item in self.bom_items.all().select_related('sub_part'):
            if not item.sub_part.has_pricing_info:
                return False

        return True

    def get_price_info(self, quantity=1, buy=True, bom=True):
        """ Return a simplified pricing string for this part
        
        Args:
            quantity: Number of units to calculate price for
            buy: Include supplier pricing (default = True)
            bom: Include BOM pricing (default = True)
        """

        price_range = self.get_price_range(quantity, buy, bom)

        if price_range is None:
            return None

        min_price, max_price = price_range

        if min_price == max_price:
            return min_price

        return "{a} - {b}".format(a=min_price, b=max_price)

    def get_supplier_price_range(self, quantity=1):

        min_price = None
        max_price = None

        for supplier in self.supplier_parts.all():

            price = supplier.get_price(quantity)

            if price is None:
                continue

            if min_price is None or price < min_price:
                min_price = price

            if max_price is None or price > max_price:
                max_price = price

        if min_price is None or max_price is None:
            return None

        return (min_price, max_price)

    def get_bom_price_range(self, quantity=1):
        """ Return the price range of the BOM for this part.
        Adds the minimum price for all components in the BOM.

        Note: If the BOM contains items without pricing information,
        these items cannot be included in the BOM!
        """

        min_price = None
        max_price = None

        for item in self.bom_items.all().select_related('sub_part'):
            prices = item.sub_part.get_price_range(quantity * item.quantity)

            if prices is None:
                continue

            low, high = prices

            if min_price is None:
                min_price = 0

            if max_price is None:
                max_price = 0

            min_price += low
            max_price += high

        if min_price is None or max_price is None:
            return None

        return (min_price, max_price)

    def get_price_range(self, quantity=1, buy=True, bom=True):
        """ Return the price range for this part. This price can be either:

        - Supplier price (if purchased from suppliers)
        - BOM price (if built from other parts)

        Returns:
            Minimum of the supplier price or BOM price. If no pricing available, returns None
        """

        buy_price_range = self.get_supplier_price_range(
            quantity) if buy else None
        bom_price_range = self.get_bom_price_range(quantity) if bom else None

        if buy_price_range is None:
            return bom_price_range

        elif bom_price_range is None:
            return buy_price_range

        else:
            return (min(buy_price_range[0], bom_price_range[0]),
                    max(buy_price_range[1], bom_price_range[1]))

    def deepCopy(self, other, **kwargs):
        """ Duplicates non-field data from another part.
        Does not alter the normal fields of this part,
        but can be used to copy other data linked by ForeignKey refernce.

        Keyword Args:
            image: If True, copies Part image (default = True)
            bom: If True, copies BOM data (default = False)
        """

        # Copy the part image
        if kwargs.get('image', True):
            if other.image:
                image_file = ContentFile(other.image.read())
                image_file.name = rename_part_image(self, other.image.url)

                self.image = image_file

        # Copy the BOM data
        if kwargs.get('bom', False):
            for item in other.bom_items.all():
                # Point the item to THIS part.
                # Set the pk to None so a new entry is created.
                item.part = self
                item.pk = None
                item.save()

        # Copy the fields that aren't available in the duplicate form
        self.salable = other.salable
        self.assembly = other.assembly
        self.component = other.component
        self.purchaseable = other.purchaseable
        self.trackable = other.trackable
        self.virtual = other.virtual

        self.save()

    @property
    def attachment_count(self):
        """ Count the number of attachments for this part.
        If the part is a variant of a template part,
        include the number of attachments for the template part.

        """

        n = self.attachments.count()

        if self.variant_of:
            n += self.variant_of.attachments.count()

        return n

    def purchase_orders(self):
        """ Return a list of purchase orders which reference this part """

        orders = []

        for part in self.supplier_parts.all().prefetch_related(
                'purchase_order_line_items'):
            for order in part.purchase_orders():
                if order not in orders:
                    orders.append(order)

        return orders

    def open_purchase_orders(self):
        """ Return a list of open purchase orders against this part """

        return [
            order for order in self.purchase_orders()
            if order.status in OrderStatus.OPEN
        ]

    def closed_purchase_orders(self):
        """ Return a list of closed purchase orders against this part """

        return [
            order for order in self.purchase_orders()
            if order.status not in OrderStatus.OPEN
        ]

    @property
    def on_order(self):
        """ Return the total number of items on order for this part. """

        return sum([
            part.on_order()
            for part in self.supplier_parts.all().prefetch_related(
                'purchase_order_line_items')
        ])

    def get_parameters(self):
        """ Return all parameters for this part, ordered by name """

        return self.parameters.order_by('template__name')
Esempio n. 25
0
class productoTerminado(models.Model):
	id=models.AutoField(primary_key=True)
	orden=models.ForeignKey(Orden, null=True, blank=True, on_delete=models.CASCADE)
	cantidadProducto=models.IntegerField()
	costoUnitarioProducto=models.DecimalField('Costo Unitario', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	costoTotalProducto=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	porcentajeGanancia=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	precioVenta=models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 26
0
class Invoice(core_models.UuidMixin, models.Model):
    """ Invoice describes billing information about purchased packages for customers on a monthly basis """
    class Permissions(object):
        customer_path = 'customer'

    class Meta(object):
        unique_together = ('customer', 'month', 'year')

    class States(object):
        PENDING = 'pending'
        CREATED = 'created'
        PAID = 'paid'
        CANCELED = 'canceled'

        CHOICES = ((PENDING, _('Pending')), (CREATED, _('Created')),
                   (PAID, _('Paid')), (CANCELED, _('Canceled')))

    month = models.PositiveSmallIntegerField(
        default=utils.get_current_month,
        validators=[MinValueValidator(1),
                    MaxValueValidator(12)])
    year = models.PositiveSmallIntegerField(default=utils.get_current_year)
    state = models.CharField(max_length=30,
                             choices=States.CHOICES,
                             default=States.PENDING)
    customer = models.ForeignKey(structure_models.Customer,
                                 verbose_name=_('organization'),
                                 related_name='+',
                                 on_delete=models.CASCADE)
    current_cost = models.DecimalField(
        default=0,
        max_digits=10,
        decimal_places=2,
        help_text=_('Cached value for current cost.'),
        editable=False)
    tax_percent = models.DecimalField(
        default=0,
        max_digits=4,
        decimal_places=2,
        validators=[MinValueValidator(0),
                    MaxValueValidator(100)])
    invoice_date = models.DateField(
        null=True,
        blank=True,
        help_text=_('Date then invoice moved from state pending to created.'))

    tracker = FieldTracker()

    def update_current_cost(self):
        self.current_cost = self.total_current
        self.save(update_fields=['current_cost'])

    @property
    def tax(self):
        return self.price * self.tax_percent / 100

    @property
    def total(self):
        return self.price + self.tax

    @property
    def price(self):
        return sum((item.price for item in self.items))

    @property
    def tax_current(self):
        return self.price_current * self.tax_percent / 100

    @property
    def total_current(self):
        return self.price_current + self.tax_current

    @property
    def price_current(self):
        return sum((item.price_current for item in self.items))

    @property
    def items(self):
        return itertools.chain(self.openstack_items.all(),
                               self.offering_items.all(),
                               self.generic_items.all())

    @property
    def due_date(self):
        if self.invoice_date:
            return self.invoice_date + datetime.timedelta(
                days=settings.WALDUR_INVOICES['PAYMENT_INTERVAL'])

    @property
    def number(self):
        return 100000 + self.id

    def set_created(self):
        """
        Performs following actions:
            - Freeze all invoice items
            - Change state from pending to billed
        """
        if self.state != self.States.PENDING:
            raise IncorrectStateException(
                _('Invoice must be in pending state.'))

        self.state = self.States.CREATED
        self.invoice_date = timezone.now().date()
        self.save(update_fields=['state', 'invoice_date'])

    def freeze(self):
        for item in self.items:
            item.freeze()

    def register_offering(self, offering, start=None):
        if start is None:
            start = timezone.now()

        end = core_utils.month_end(start)
        OfferingItem.objects.create(
            offering=offering,
            unit_price=offering.unit_price,
            unit=offering.unit,
            invoice=self,
            start=start,
            end=end,
        )

    def __str__(self):
        return '%s | %s-%s' % (self.customer, self.year, self.month)
Esempio n. 27
0
class detalleTransaccion(models.Model):
	id_detalle = models.AutoField(primary_key = True)
	debe = models.DecimalField('debe', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	haber = models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])  
	id_Transaccion = models.ForeignKey(Transaccion, null=True, blank=True,on_delete= models.CASCADE)
	id_cuenta = models.ForeignKey(Cuenta, null=True, blank=True, on_delete=models.CASCADE)
	def __str__(self):
		return '{}{}'.format(self.id_detalle)
Esempio n. 28
0
 (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
 (validate_comma_separated_integer_list, ',', ValidationError),
 (validate_comma_separated_integer_list, '1,2,3,', ValidationError),
 (validate_comma_separated_integer_list, '1,2,', ValidationError),
 (validate_comma_separated_integer_list, ',1', ValidationError),
 (validate_comma_separated_integer_list, '1,,2', ValidationError),
 (int_list_validator(sep='.'), '1.2.3', None),
 (int_list_validator(sep='.'), '1,2,3', ValidationError),
 (MaxValueValidator(10), 10, None),
 (MaxValueValidator(10), -10, None),
 (MaxValueValidator(10), 0, None),
 (MaxValueValidator(NOW), NOW, None),
 (MaxValueValidator(NOW), NOW - timedelta(days=1), None),
 (MaxValueValidator(0), 1, ValidationError),
 (MaxValueValidator(NOW), NOW + timedelta(days=1), ValidationError),
 (MinValueValidator(-10), -10, None),
 (MinValueValidator(-10), 10, None),
 (MinValueValidator(-10), 0, None),
 (MinValueValidator(NOW), NOW, None),
 (MinValueValidator(NOW), NOW + timedelta(days=1), None),
 (MinValueValidator(0), -1, ValidationError),
 (MinValueValidator(NOW), NOW - timedelta(days=1), ValidationError),
 (MaxLengthValidator(10), '', None),
 (MaxLengthValidator(10), 10 * 'x', None),
 (MaxLengthValidator(10), 15 * 'x', ValidationError),
 (MinLengthValidator(10), 15 * 'x', None),
 (MinLengthValidator(10), 10 * 'x', None),
 (MinLengthValidator(10), '', ValidationError),
 (URLValidator(EXTENDED_SCHEMES), 'file://localhost/path', None),
 (URLValidator(EXTENDED_SCHEMES), 'git://example.com/', None),
 (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError),
Esempio n. 29
0
class estadoResulta(models.Model):
	id=models.AutoField(primary_key=True)
	debe =models.DecimalField('debe', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	haber= models.DecimalField('haber', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	utilidades=models.DecimalField('Utilildad', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
	utilidadNeta=models.DecimalField('Utilildad', max_digits=50, decimal_places=2, blank=False, null=True, validators=[MinValueValidator(0)])
Esempio n. 30
0
class Settings(Model):
    stg_guicertificate = models.ForeignKey(
        "Certificate",
        verbose_name=_("Certificate for HTTPS"),
        limit_choices_to={
            'cert_type__in': [CERT_TYPE_EXISTING, CERT_TYPE_INTERNAL]
        },
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    stg_guiaddress = ListField(blank=True,
                               default=['0.0.0.0'],
                               verbose_name=_("WebGUI IPv4 Address"))
    stg_guiv6address = ListField(blank=True,
                                 default=['::'],
                                 verbose_name=_("WebGUI IPv6 Address"))
    stg_guiport = models.IntegerField(
        verbose_name=_("WebGUI HTTP Port"),
        validators=[MinValueValidator(1),
                    MaxValueValidator(65535)],
        default=80,
    )
    stg_guihttpsport = models.IntegerField(
        verbose_name=_("WebGUI HTTPS Port"),
        validators=[MinValueValidator(1),
                    MaxValueValidator(65535)],
        default=443,
    )
    stg_guihttpsredirect = models.BooleanField(
        verbose_name=_('WebGUI HTTP -> HTTPS Redirect'),
        default=False,
        help_text=_('Redirect all incoming HTTP requests to HTTPS'),
    )
    stg_language = models.CharField(max_length=120,
                                    choices=settings.LANGUAGES,
                                    default="en",
                                    verbose_name=_("Language"))
    stg_kbdmap = models.CharField(
        max_length=120,
        choices=choices.KBDMAP_CHOICES(),
        verbose_name=_("Console Keyboard Map"),
        blank=True,
    )
    stg_timezone = models.CharField(max_length=120,
                                    choices=choices.TimeZoneChoices(),
                                    default="America/Los_Angeles",
                                    verbose_name=_("Timezone"))
    stg_sysloglevel = models.CharField(
        max_length=120,
        choices=choices.SYS_LOG_LEVEL,
        default="f_info",
        verbose_name=_("Syslog level"),
        help_text=_("Specifies which messages will be logged by "
                    "server. INFO and VERBOSE log transactions that "
                    "server performs on behalf of the client. "
                    "f_is_debug specify higher levels of debugging output. "
                    "The default is f_info."),
    )
    stg_syslogserver = models.CharField(
        default='',
        blank=True,
        max_length=120,
        verbose_name=_("Syslog server"),
        help_text=_(
            "Specifies the server and port syslog messages "
            "will be sent to.  The accepted format is hostname:port "
            "or ip:port, if :port is not specified it will default to "
            "port 514 (this field currently only takes IPv4 addresses)"),
    )
    stg_wizardshown = models.BooleanField(
        editable=False,
        default=False,
    )
    stg_pwenc_check = models.CharField(
        max_length=100,
        editable=False,
    )

    class Meta:
        verbose_name = _("General")