Browse Source

Add copy feature to documents

Jocelyn Delande 9 years ago
parent
commit
4ef3b71018
3 changed files with 127 additions and 1 deletions
  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
 from itertools import chain
 
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
-from django.db import models
+from django.db import models, transaction
 
 
 class Document(models.Model):
@@ -27,6 +28,49 @@ class Document(models.Model):
     def get_absolute_url(self):
         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):
     name = models.CharField(max_length=130)

+ 20 - 0
costs/tests/test_models.py

@@ -343,3 +343,23 @@ class AbstractUseTests(TestCase):
 
         with self.assertRaises(ValidationError):
             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)