Browse Source

Add copy feature to documents

Jocelyn Delande 3 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 @@
1
+- fields: {comment: '', date: 2016-03-08, name: Services, type: fact}
2
+  model: costs.document
3
+  pk: 4
4
+- fields: {capacity_unit: a, description: '', document: 4, name: Electricity contract,
5
+    price: 100.0, total_capacity: 2.0}
6
+  model: costs.cost
7
+  pk: 13
8
+- fields: {capacity_unit: u, description: "(ne pas oublier qu'1 switch et 1 routeur
9
+      occupent d\xE9j\xE0 2U)", document: 4, name: Espace en baie, price: 50.0, total_capacity: 10.0}
10
+  model: costs.cost
11
+  pk: 15
12
+- fields: {capacity_unit: mbps, description: '', document: 4, name: IP transit, price: 10.0,
13
+    total_capacity: 50.0}
14
+  model: costs.cost
15
+  pk: 16
16
+- fields: {capacity_unit: '', description: '', document: 4, name: core router, price: 150.0,
17
+    provisioning_duration: '1095 00:00:00', total_capacity: 1.0}
18
+  model: costs.good
19
+  pk: 22
20
+- fields: {capacity_unit: eth, description: '', document: 4, name: 24 ports switch,
21
+    price: 100.0, provisioning_duration: '1095 00:00:00', total_capacity: 24.0}
22
+  model: costs.good
23
+  pk: 23
24
+- fields: {capacity_unit: u, description: '', document: 4, name: setup fee bay, price: 200.0,
25
+    provisioning_duration: '1825 00:00:00', total_capacity: 10.0}
26
+  model: costs.good
27
+  pk: 24
28
+- fields: {resource: 13, service: 13, share: 0.4}
29
+  model: costs.costuse
30
+  pk: 25
31
+- fields: {resource: 15, service: 13, share: 2.0}
32
+  model: costs.costuse
33
+  pk: 26
34
+- fields: {resource: 16, service: 13, share: 5.0}
35
+  model: costs.costuse
36
+  pk: 27
37
+- fields: {resource: 13, service: 19, share: 1.0}
38
+  model: costs.costuse
39
+  pk: 44
40
+- fields: {resource: 15, service: 19, share: 2.0}
41
+  model: costs.costuse
42
+  pk: 45
43
+- fields: {resource: 23, service: 13, share: 2.0}
44
+  model: costs.gooduse
45
+  pk: 25
46
+- fields: {resource: 22, service: 13, share: 0.05}
47
+  model: costs.gooduse
48
+  pk: 26
49
+- fields: {resource: 24, service: 13, share: 2.0}
50
+  model: costs.gooduse
51
+  pk: 27
52
+- fields: {resource: 24, service: 19, share: 2.0}
53
+  model: costs.gooduse
54
+  pk: 46
55
+- fields: {capacity_unit: '', description: '', document: 4, name: 1U server hosting,
56
+    reusable: false, subscriptions_count: 2, total_capacity: 1.0}
57
+  model: costs.service
58
+  pk: 13
59
+- fields: {capacity_unit: '', description: '', document: 4, name: 1U sound machine,
60
+    reusable: false, subscriptions_count: 2, total_capacity: 1.0}
61
+  model: costs.service
62
+  pk: 19

+ 45 - 1
costs/models.py

@@ -1,10 +1,11 @@
1
+import copy
1 2
 import datetime
2 3
 from itertools import chain
3 4
 
4 5
 from django.conf import settings
5 6
 from django.core.exceptions import ValidationError
6 7
 from django.core.urlresolvers import reverse
7
-from django.db import models
8
+from django.db import models, transaction
8 9
 
9 10
 
10 11
 class Document(models.Model):
@@ -27,6 +28,49 @@ class Document(models.Model):
27 28
     def get_absolute_url(self):
28 29
         return reverse('detail-document', kwargs={'pk': self.pk})
29 30
 
31
+    def copy(self):
32
+        """ Deep copy and saving of a document
33
+
34
+        All related resources are copied.
35
+        """
36
+        with transaction.atomic():
37
+            new_doc = Document.objects.get(pk=self.pk)
38
+            new_doc.pk = None
39
+            new_doc.name = 'COPIE DE {}'.format(new_doc.name)
40
+            new_doc.save()
41
+
42
+            new_services, new_costs, new_goods = {}, {}, {}
43
+
44
+            to_copy = (
45
+                (self.service_set, new_services),
46
+                (self.good_set, new_goods),
47
+                (self.cost_set, new_costs),
48
+            )
49
+
50
+            for qs, index in to_copy:
51
+                for item in qs.all():
52
+                    old_pk = item.pk
53
+                    item.pk = None
54
+                    item.document = new_doc
55
+                    item.save()
56
+                    index[old_pk] = item
57
+
58
+            to_reproduce = (
59
+                (ServiceUse, new_services),
60
+                (GoodUse, new_goods),
61
+                (CostUse, new_costs),
62
+            )
63
+
64
+            for Klass, index in to_reproduce:
65
+                src_qs = Klass.objects.filter(service__document=self)
66
+                for use in src_qs.all():
67
+                    use.pk = None
68
+                    use.service = new_services[use.service.pk]
69
+                    use.resource = index[use.resource.pk]
70
+                    use.save()
71
+
72
+            return new_doc
73
+
30 74
 
31 75
 class AbstractItem(models.Model):
32 76
     name = models.CharField(max_length=130)

+ 20 - 0
costs/tests/test_models.py

@@ -343,3 +343,23 @@ class AbstractUseTests(TestCase):
343 343
 
344 344
         with self.assertRaises(ValidationError):
345 345
             create_clean(c, a)
346
+
347
+
348
+class TestDocuments(TestCase):
349
+    fixtures = ['full_example.yaml']
350
+
351
+    _all_models = [Document, Cost, Good, Service, GoodUse, CostUse, ServiceUse]
352
+
353
+    def _count(self):
354
+        return [i.objects.count() for i in self._all_models]
355
+
356
+    def test_copy(self):
357
+        initial_counts = self._count()
358
+
359
+        old_doc = Document.objects.first()
360
+        new_doc = old_doc.copy()
361
+
362
+        self.assertNotEqual(new_doc.pk, old_doc.pk)
363
+        self.assertEqual(self._count(), [i*2 for i in initial_counts])
364
+        old_doc.delete()
365
+        self.assertEqual(self._count(), initial_counts)