diff --git a/src/teams/models.py b/src/teams/models.py index 6e0862f4..3366926f 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -401,7 +401,7 @@ class TeamShift(CampRelatedModel): shift_range = DateTimeRangeField() - team_members = models.ManyToManyField(TeamMember, blank=True) + team_members = models.ManyToManyField(TeamMember, blank=True, through=TeamShiftAssignment) people_required = models.IntegerField(default=1) @@ -420,3 +420,21 @@ class TeamShift(CampRelatedModel): @property def users(self): return [member.user for member in self.team_members.all()] + +class TeamShiftAssignment(CampRelatedModel): + team_shift = models.ForeignKey( + "teams.TeamShift", + on_delete=models.CASCADE, + help_text="The shift", + ) + + team_member = models.ForeignKey( + "teams.TeamMember", + on_delete=models.CASCADE, + help_text="The team member on shift", + ) + + for_sale = models.BooleanField( + default=False, + help_text="Is the shift assignment for sale?", + ) diff --git a/src/teams/templates/team_shift_list.html b/src/teams/templates/team_shift_list.html index 360e66a9..e8bbf025 100644 --- a/src/teams/templates/team_shift_list.html +++ b/src/teams/templates/team_shift_list.html @@ -50,7 +50,7 @@ Shifts | {{ block.super }} {{ shift.people_required }} {% for member in shift.team_members.all %} - {{ member.user.profile.get_public_credit_name }}{% if not forloop.last %},{% endif %} + {{ member.user.profile.get_public_credit_name }}{% if member.for_sale %} (for sale!){% endif %}{% if not forloop.last %},{% endif %} {% empty %} None! {% endfor %} @@ -71,7 +71,11 @@ Shifts | {{ block.super }} href="{% url 'teams:shift_member_drop' camp_slug=camp.slug team_slug=team.slug pk=shift.pk %}"> Unassign me - {% elif shift.people_required > shift.team_members.count %} + + Sell shift + + {% elif shift.people_required > shift.team_members.filter(for_sale=False).count %} Assign me diff --git a/src/teams/templates/team_user_shifts.html b/src/teams/templates/team_user_shifts.html index 745fd2bb..e3311b90 100644 --- a/src/teams/templates/team_user_shifts.html +++ b/src/teams/templates/team_user_shifts.html @@ -34,6 +34,10 @@ Your shifts | {{ block.super }} {{ shift.shift_range.upper|date:'H:i' }} + + + Sell shift @@ -45,4 +49,4 @@ Your shifts | {{ block.super }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/teams/urls.py b/src/teams/urls.py index 92ef4e03..33bdc7c7 100644 --- a/src/teams/urls.py +++ b/src/teams/urls.py @@ -179,6 +179,11 @@ urlpatterns = [ MemberDropsShift.as_view(), name="shift_member_drop", ), + path( + "sell", + MemberSellsShift.as_view(), + name="shift_member_sell", + ), ] ), ), diff --git a/src/teams/views/shifts.py b/src/teams/views/shifts.py index eaa44fc8..72d0ede9 100644 --- a/src/teams/views/shifts.py +++ b/src/teams/views/shifts.py @@ -274,6 +274,9 @@ class MemberTakesShift(LoginRequiredMixin, CampViewMixin, View): request, template.render(Context({"shifts": overlapping_shifts})) ) else: + # Remove at most one shift assignment for sale + for shift_assignment in shift.team_members.filter(for_sale=True)[:1]: + shift_assignment.delete() shift.team_members.add(team_member) kwargs.pop("pk") @@ -297,6 +300,23 @@ class MemberDropsShift(LoginRequiredMixin, CampViewMixin, View): return HttpResponseRedirect(reverse("teams:shifts", kwargs=kwargs)) +class MemberSellsShift(LoginRequiredMixin, CampViewMixin, View): + + http_methods = ["get"] + + def get(self, request, **kwargs): + shift = TeamShift.objects.get(id=kwargs["pk"]) + team = Team.objects.get(camp=self.camp, slug=kwargs["team_slug"]) + + team_member = TeamMember.objects.get(team=team, user=request.user) + + shift_assignment = shift.team_members.get(team_member = team_member.pk) + shift_assignment.for_sale = True + shift_assignment.save() + + kwargs.pop("pk") + + return HttpResponseRedirect(reverse("teams:shifts", kwargs=kwargs)) class UserShifts(CampViewMixin, TemplateView): template_name = "team_user_shifts.html"