Parcourir la source

Add copy feature to documents

Jocelyn Delande il y a 9 ans
Parent
commit
4ef3b71018
3 fichiers modifiés avec 127 ajouts et 1 suppressions
  1. 62 0
      costs/fixtures/full_example.yaml
  2. 45 1
      costs/models.py
  3. 20 0
      costs/tests/test_models.py

+ 62 - 0
costs/fixtures/full_example.yaml

@@ -0,0 +1,62 @@
+- fields: {comment: '', date: 2016-03-08, name: Services, type: fact}
+  model: costs.document
+  pk: 4
+- fields: {capacity_unit: a, description: '', document: 4, name: Electricity contract,
+    price: 100.0, total_capacity: 2.0}
+  model: costs.cost
+  pk: 13
+- fields: {capacity_unit: u, description: "(ne pas oublier qu'1 switch et 1 routeur
+      occupent d\xE9j\xE0 2U)", document: 4, name: Espace en baie, price: 50.0, total_capacity: 10.0}
+  model: costs.cost
+  pk: 15
+- fields: {capacity_unit: mbps, description: '', document: 4, name: IP transit, price: 10.0,
+    total_capacity: 50.0}
+  model: costs.cost
+  pk: 16
+- fields: {capacity_unit: '', description: '', document: 4, name: core router, price: 150.0,
+    provisioning_duration: '1095 00:00:00', total_capacity: 1.0}
+  model: costs.good
+  pk: 22
+- fields: {capacity_unit: eth, description: '', document: 4, name: 24 ports switch,
+    price: 100.0, provisioning_duration: '1095 00:00:00', total_capacity: 24.0}
+  model: costs.good
+  pk: 23
+- fields: {capacity_unit: u, description: '', document: 4, name: setup fee bay, price: 200.0,
+    provisioning_duration: '1825 00:00:00', total_capacity: 10.0}
+  model: costs.good
+  pk: 24
+- fields: {resource: 13, service: 13, share: 0.4}
+  model: costs.costuse
+  pk: 25
+- fields: {resource: 15, service: 13, share: 2.0}
+  model: costs.costuse
+  pk: 26
+- fields: {resource: 16, service: 13, share: 5.0}
+  model: costs.costuse
+  pk: 27
+- fields: {resource: 13, service: 19, share: 1.0}
+  model: costs.costuse
+  pk: 44
+- fields: {resource: 15, service: 19, share: 2.0}
+  model: costs.costuse
+  pk: 45
+- fields: {resource: 23, service: 13, share: 2.0}
+  model: costs.gooduse
+  pk: 25
+- fields: {resource: 22, service: 13, share: 0.05}
+  model: costs.gooduse
+  pk: 26
+- fields: {resource: 24, service: 13, share: 2.0}
+  model: costs.gooduse
+  pk: 27
+- fields: {resource: 24, service: 19, share: 2.0}
+  model: costs.gooduse
+  pk: 46
+- fields: {capacity_unit: '', description: '', document: 4, name: 1U server hosting,
+    reusable: false, subscriptions_count: 2, total_capacity: 1.0}
+  model: costs.service
+  pk: 13
+- fields: {capacity_unit: '', description: '', document: 4, name: 1U sound machine,
+    reusable: false, subscriptions_count: 2, total_capacity: 1.0}
+  model: costs.service
+  pk: 19

+ 45 - 1
costs/models.py

@@ -1,10 +1,11 @@
+import copy
 import datetime
 import datetime
 from itertools import chain
 from itertools import chain
 
 
 from django.conf import settings
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
-from django.db import models
+from django.db import models, transaction
 
 
 
 
 class Document(models.Model):
 class Document(models.Model):
@@ -27,6 +28,49 @@ class Document(models.Model):
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('detail-document', kwargs={'pk': self.pk})
         return reverse('detail-document', kwargs={'pk': self.pk})
 
 
+    def copy(self):
+        """ Deep copy and saving of a document
+
+        All related resources are copied.
+        """
+        with transaction.atomic():
+            new_doc = Document.objects.get(pk=self.pk)
+            new_doc.pk = None
+            new_doc.name = 'COPIE DE {}'.format(new_doc.name)
+            new_doc.save()
+
+            new_services, new_costs, new_goods = {}, {}, {}
+
+            to_copy = (
+                (self.service_set, new_services),
+                (self.good_set, new_goods),
+                (self.cost_set, new_costs),
+            )
+
+            for qs, index in to_copy:
+                for item in qs.all():
+                    old_pk = item.pk
+                    item.pk = None
+                    item.document = new_doc
+                    item.save()
+                    index[old_pk] = item
+
+            to_reproduce = (
+                (ServiceUse, new_services),
+                (GoodUse, new_goods),
+                (CostUse, new_costs),
+            )
+
+            for Klass, index in to_reproduce:
+                src_qs = Klass.objects.filter(service__document=self)
+                for use in src_qs.all():
+                    use.pk = None
+                    use.service = new_services[use.service.pk]
+                    use.resource = index[use.resource.pk]
+                    use.save()
+
+            return new_doc
+
 
 
 class AbstractItem(models.Model):
 class AbstractItem(models.Model):
     name = models.CharField(max_length=130)
     name = models.CharField(max_length=130)

+ 20 - 0
costs/tests/test_models.py

@@ -343,3 +343,23 @@ class AbstractUseTests(TestCase):
 
 
         with self.assertRaises(ValidationError):
         with self.assertRaises(ValidationError):
             create_clean(c, a)
             create_clean(c, a)
+
+
+class TestDocuments(TestCase):
+    fixtures = ['full_example.yaml']
+
+    _all_models = [Document, Cost, Good, Service, GoodUse, CostUse, ServiceUse]
+
+    def _count(self):
+        return [i.objects.count() for i in self._all_models]
+
+    def test_copy(self):
+        initial_counts = self._count()
+
+        old_doc = Document.objects.first()
+        new_doc = old_doc.copy()
+
+        self.assertNotEqual(new_doc.pk, old_doc.pk)
+        self.assertEqual(self._count(), [i*2 for i in initial_counts])
+        old_doc.delete()
+        self.assertEqual(self._count(), initial_counts)