We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
shipping address shouldn't be editable while order is processed?
Order has a foreign key to address for shipping address.  
With the current model, I think a user is able to change the address while the order is being processed (before shipped without realizing his past order might be affected)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/checkout/core.py`
Content:
```
1 from __future__ import unicode_literals
2 from functools import wraps
3 
4 from django.conf import settings
5 from django.db import transaction
6 from django.forms.models import model_to_dict
7 from django.utils.encoding import smart_text
8 from prices import Price, FixedDiscount
9 
10 from ..cart.views import get_or_empty_db_cart
11 from ..core import analytics
12 from ..discount.models import Voucher, NotApplicable
13 from ..order.models import Order
14 from ..shipping.models import ShippingMethodCountry, ANY_COUNTRY
15 from ..userprofile.models import Address, User
16 
17 STORAGE_SESSION_KEY = 'checkout_storage'
18 
19 
20 class Checkout(object):
21 
22     VERSION = '1.0.0'
23     modified = False
24 
25     def __init__(self, cart, user, tracking_code):
26         self.cart = cart
27         self.storage = {'version': self.VERSION}
28         self.tracking_code = tracking_code
29         self.user = user
30         self.discounts = cart.discounts
31 
32     @classmethod
33     def from_storage(cls, storage_data, cart, user, tracking_code):
34         checkout = cls(cart, user, tracking_code)
35         checkout.storage = storage_data
36         try:
37             version = checkout.storage['version']
38         except (TypeError, KeyError):
39             version = None
40         if version != cls.VERSION:
41             checkout.storage = {'version': cls.VERSION}
42         return checkout
43 
44     def for_storage(self):
45         return self.storage
46 
47     def clear_storage(self):
48         self.storage = None
49         self.modified = True
50 
51     def _get_address_from_storage(self, key):
52         address_data = self.storage.get(key)
53         if address_data is not None and address_data.get('id'):
54             try:
55                 return Address.objects.get(id=address_data['id'])
56             except Address.DoesNotExist:
57                 return None
58         elif address_data:
59             return Address(**address_data)
60         return None
61 
62     @property
63     def is_shipping_required(self):
64         return self.cart.is_shipping_required()
65 
66     @property
67     def deliveries(self):
68         for partition in self.cart.partition():
69             if self.shipping_method and partition.is_shipping_required():
70                 shipping_cost = self.shipping_method.get_total()
71             else:
72                 shipping_cost = Price(0, currency=settings.DEFAULT_CURRENCY)
73             total_with_shipping = partition.get_total(
74                 discounts=self.cart.discounts) + shipping_cost
75 
76             partition = [
77                 (item,
78                  item.get_price_per_item(discounts=self.cart.discounts),
79                  item.get_total(discounts=self.cart.discounts))
80                 for item in partition]
81 
82             yield partition, shipping_cost, total_with_shipping
83 
84     @property
85     def shipping_address(self):
86         address = self._get_address_from_storage('shipping_address')
87         if address is None and self.user.is_authenticated():
88             return self.user.default_shipping_address
89         return address
90 
91     @shipping_address.setter
92     def shipping_address(self, address):
93         address_data = model_to_dict(address)
94         address_data['country'] = smart_text(address_data['country'])
95         self.storage['shipping_address'] = address_data
96         self.modified = True
97 
98     @property
99     def shipping_method(self):
100         shipping_address = self.shipping_address
101         if shipping_address is not None:
102             shipping_method_country_id = self.storage.get(
103                 'shipping_method_country_id')
104             if shipping_method_country_id is not None:
105                 try:
106                     shipping_method_country = ShippingMethodCountry.objects.get(
107                         id=shipping_method_country_id)
108                 except ShippingMethodCountry.DoesNotExist:
109                     return None
110                 shipping_country_code = shipping_address.country.code
111                 if (shipping_method_country.country_code == ANY_COUNTRY or
112                         shipping_method_country.country_code == shipping_country_code):
113                     return shipping_method_country
114 
115     @shipping_method.setter
116     def shipping_method(self, shipping_method_country):
117         self.storage['shipping_method_country_id'] = shipping_method_country.id
118         self.modified = True
119 
120     @property
121     def email(self):
122         return self.storage.get('email')
123 
124     @email.setter
125     def email(self, email):
126         self.storage['email'] = email
127         self.modified = True
128 
129     @property
130     def billing_address(self):
131         address = self._get_address_from_storage('billing_address')
132         if address is not None:
133             return address
134         elif self.user.is_authenticated() and self.user.default_billing_address:
135             return self.user.default_billing_address
136         elif self.shipping_address:
137             return self.shipping_address
138 
139     @billing_address.setter
140     def billing_address(self, address):
141         address_data = model_to_dict(address)
142         address_data['country'] = smart_text(address_data['country'])
143         self.storage['billing_address'] = address_data
144         self.modified = True
145 
146     @property
147     def discount(self):
148         value = self.storage.get('discount_value')
149         currency = self.storage.get('discount_currency')
150         name = self.storage.get('discount_name')
151         if value is not None and name is not None and currency is not None:
152             amount = Price(value, currency=currency)
153             return FixedDiscount(amount, name)
154 
155     @discount.setter
156     def discount(self, discount):
157         amount = discount.amount
158         self.storage['discount_value'] = smart_text(amount.net)
159         self.storage['discount_currency'] = amount.currency
160         self.storage['discount_name'] = discount.name
161         self.modified = True
162 
163     @discount.deleter
164     def discount(self):
165         if 'discount_value' in self.storage:
166             del self.storage['discount_value']
167             self.modified = True
168         if 'discount_currency' in self.storage:
169             del self.storage['discount_currency']
170             self.modified = True
171         if 'discount_name' in self.storage:
172             del self.storage['discount_name']
173             self.modified = True
174 
175     @property
176     def voucher_code(self):
177         return self.storage.get('voucher_code')
178 
179     @voucher_code.setter
180     def voucher_code(self, voucher_code):
181         self.storage['voucher_code'] = voucher_code
182         self.modified = True
183 
184     @voucher_code.deleter
185     def voucher_code(self):
186         if 'voucher_code' in self.storage:
187             del self.storage['voucher_code']
188             self.modified = True
189 
190     @property
191     def is_shipping_same_as_billing(self):
192         return Address.objects.are_identical(
193             self.shipping_address, self.billing_address)
194 
195     def _save_address(self, address, is_billing=False, is_shipping=False):
196         if self.user.is_authenticated() and address.id is None:
197             address = User.objects.store_address(
198                 self.user, address, shipping=is_shipping, billing=is_billing)
199         elif address.id is None:
200             address.save()
201         return address
202 
203     @transaction.atomic
204     def create_order(self):
205         if self.is_shipping_required:
206             shipping_address = self._save_address(
207                 self.shipping_address, is_shipping=True)
208         else:
209             shipping_address = None
210         billing_address = self._save_address(
211             self.billing_address, is_billing=True)
212 
213         order_data = {
214             'billing_address': billing_address,
215             'shipping_address': shipping_address,
216             'tracking_client_id': self.tracking_code,
217             'total': self.get_total()}
218 
219         if self.user.is_authenticated():
220             order_data['user'] = self.user
221         else:
222             # TODO: we should always save email in order not only
223             # for anonymous
224             order_data['anonymous_user_email'] = self.email
225 
226         voucher = self._get_voucher()
227         if voucher is not None:
228             discount = self.discount
229             order_data['voucher'] = voucher
230             order_data['discount_amount'] = discount.amount
231             order_data['discount_name'] = discount.name
232 
233         order = Order.objects.create(**order_data)
234 
235         for partition in self.cart.partition():
236             shipping_required = partition.is_shipping_required()
237             if shipping_required:
238                 shipping_price = self.shipping_method.get_total()
239                 shipping_method_name = smart_text(self.shipping_method)
240             else:
241                 shipping_price = 0
242                 shipping_method_name = None
243             group = order.groups.create(
244                 shipping_price=shipping_price,
245                 shipping_method_name=shipping_method_name)
246             group.add_items_from_partition(
247                 partition, discounts=self.cart.discounts)
248 
249         if voucher is not None:
250             Voucher.objects.increase_usage(voucher)
251 
252         return order
253 
254     def _get_voucher(self):
255         voucher_code = self.voucher_code
256         if voucher_code is not None:
257             vouchers = Voucher.objects.active().select_for_update()
258             try:
259                 return vouchers.get(code=self.voucher_code)
260             except Voucher.DoesNotExist:
261                 return None
262 
263     def recalculate_discount(self):
264         voucher = self._get_voucher()
265         if voucher is not None:
266             try:
267                 self.discount = voucher.get_discount_for_checkout(self)
268             except NotApplicable:
269                 del self.discount
270                 del self.voucher_code
271         else:
272             del self.discount
273             del self.voucher_code
274 
275     def get_subtotal(self):
276         zero = Price(0, currency=settings.DEFAULT_CURRENCY)
277         cost_iterator = (
278             total - shipping_cost
279             for shipment, shipping_cost, total in self.deliveries)
280         total = sum(cost_iterator, zero)
281         return total
282 
283     def get_total(self):
284         zero = Price(0, currency=settings.DEFAULT_CURRENCY)
285         cost_iterator = (
286             total
287             for shipment, shipping_cost, total in self.deliveries)
288         total = sum(cost_iterator, zero)
289         return total if self.discount is None else self.discount.apply(total)
290 
291     def get_total_shipping(self):
292         zero = Price(0, currency=settings.DEFAULT_CURRENCY)
293         cost_iterator = (
294             shipping_cost
295             for shipment, shipping_cost, total in self.deliveries)
296         total = sum(cost_iterator, zero)
297         return total
298 
299 
300 def load_checkout(view):
301     @wraps(view)
302     @get_or_empty_db_cart
303     def func(request, cart):
304         try:
305             session_data = request.session[STORAGE_SESSION_KEY]
306         except KeyError:
307             session_data = ''
308         tracking_code = analytics.get_client_id(request)
309         
310         checkout = Checkout.from_storage(
311             session_data, cart, request.user, tracking_code)
312         response = view(request, checkout, cart)
313         if checkout.modified:
314             request.session[STORAGE_SESSION_KEY] = checkout.for_storage()
315         return response
316 
317     return func
318
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/saleor/checkout/core.py b/saleor/checkout/core.py
--- a/saleor/checkout/core.py
+++ b/saleor/checkout/core.py
@@ -192,23 +192,36 @@
         return Address.objects.are_identical(
             self.shipping_address, self.billing_address)
 
-    def _save_address(self, address, is_billing=False, is_shipping=False):
-        if self.user.is_authenticated() and address.id is None:
-            address = User.objects.store_address(
-                self.user, address, shipping=is_shipping, billing=is_billing)
-        elif address.id is None:
-            address.save()
+    def _add_to_user_address_book(self, address, is_billing=False,
+                                  is_shipping=False):
+        if self.user.is_authenticated():
+            User.objects.store_address(
+                self.user, address, shipping=is_shipping,
+                billing=is_billing)
+
+    def _get_address_copy(self, address):
+        address.user = None
+        address.pk = None
+        address.save()
         return address
 
+    def _save_order_billing_address(self):
+        return self._get_address_copy(self.billing_address)
+
+    def _save_order_shipping_address(self):
+        return self._get_address_copy(self.shipping_address)
+
     @transaction.atomic
     def create_order(self):
         if self.is_shipping_required:
-            shipping_address = self._save_address(
+            shipping_address = self._save_order_shipping_address()
+            self._add_to_user_address_book(
                 self.shipping_address, is_shipping=True)
         else:
             shipping_address = None
-        billing_address = self._save_address(
-            self.billing_address, is_billing=True)
+        billing_address = self._save_order_billing_address()
+        self._add_to_user_address_book(
+            self.shipping_address, is_billing=True)
 
         order_data = {
             'billing_address': billing_address,
@@ -306,7 +319,7 @@
         except KeyError:
             session_data = ''
         tracking_code = analytics.get_client_id(request)
-        
+
         checkout = Checkout.from_storage(
             session_data, cart, request.user, tracking_code)
         response = view(request, checkout, cart)

{"golden_diff": "diff --git a/saleor/checkout/core.py b/saleor/checkout/core.py\n--- a/saleor/checkout/core.py\n+++ b/saleor/checkout/core.py\n@@ -192,23 +192,36 @@\n         return Address.objects.are_identical(\n             self.shipping_address, self.billing_address)\n \n-    def _save_address(self, address, is_billing=False, is_shipping=False):\n-        if self.user.is_authenticated() and address.id is None:\n-            address = User.objects.store_address(\n-                self.user, address, shipping=is_shipping, billing=is_billing)\n-        elif address.id is None:\n-            address.save()\n+    def _add_to_user_address_book(self, address, is_billing=False,\n+                                  is_shipping=False):\n+        if self.user.is_authenticated():\n+            User.objects.store_address(\n+                self.user, address, shipping=is_shipping,\n+                billing=is_billing)\n+\n+    def _get_address_copy(self, address):\n+        address.user = None\n+        address.pk = None\n+        address.save()\n         return address\n \n+    def _save_order_billing_address(self):\n+        return self._get_address_copy(self.billing_address)\n+\n+    def _save_order_shipping_address(self):\n+        return self._get_address_copy(self.shipping_address)\n+\n     @transaction.atomic\n     def create_order(self):\n         if self.is_shipping_required:\n-            shipping_address = self._save_address(\n+            shipping_address = self._save_order_shipping_address()\n+            self._add_to_user_address_book(\n                 self.shipping_address, is_shipping=True)\n         else:\n             shipping_address = None\n-        billing_address = self._save_address(\n-            self.billing_address, is_billing=True)\n+        billing_address = self._save_order_billing_address()\n+        self._add_to_user_address_book(\n+            self.shipping_address, is_billing=True)\n \n         order_data = {\n             'billing_address': billing_address,\n@@ -306,7 +319,7 @@\n         except KeyError:\n             session_data = ''\n         tracking_code = analytics.get_client_id(request)\n-        \n+\n         checkout = Checkout.from_storage(\n             session_data, cart, request.user, tracking_code)\n         response = view(request, checkout, cart)\n", "issue": "shipping address shouldn't be editable while order is processed?\nOrder has a foreign key to address for shipping address.  \nWith the current model, I think a user is able to change the address while the order is being processed (before shipped without realizing his past order might be affected)\n\n", "before_files": [{"content": "from __future__ import unicode_literals\nfrom functools import wraps\n\nfrom django.conf import settings\nfrom django.db import transaction\nfrom django.forms.models import model_to_dict\nfrom django.utils.encoding import smart_text\nfrom prices import Price, FixedDiscount\n\nfrom ..cart.views import get_or_empty_db_cart\nfrom ..core import analytics\nfrom ..discount.models import Voucher, NotApplicable\nfrom ..order.models import Order\nfrom ..shipping.models import ShippingMethodCountry, ANY_COUNTRY\nfrom ..userprofile.models import Address, User\n\nSTORAGE_SESSION_KEY = 'checkout_storage'\n\n\nclass Checkout(object):\n\n    VERSION = '1.0.0'\n    modified = False\n\n    def __init__(self, cart, user, tracking_code):\n        self.cart = cart\n        self.storage = {'version': self.VERSION}\n        self.tracking_code = tracking_code\n        self.user = user\n        self.discounts = cart.discounts\n\n    @classmethod\n    def from_storage(cls, storage_data, cart, user, tracking_code):\n        checkout = cls(cart, user, tracking_code)\n        checkout.storage = storage_data\n        try:\n            version = checkout.storage['version']\n        except (TypeError, KeyError):\n            version = None\n        if version != cls.VERSION:\n            checkout.storage = {'version': cls.VERSION}\n        return checkout\n\n    def for_storage(self):\n        return self.storage\n\n    def clear_storage(self):\n        self.storage = None\n        self.modified = True\n\n    def _get_address_from_storage(self, key):\n        address_data = self.storage.get(key)\n        if address_data is not None and address_data.get('id'):\n            try:\n                return Address.objects.get(id=address_data['id'])\n            except Address.DoesNotExist:\n                return None\n        elif address_data:\n            return Address(**address_data)\n        return None\n\n    @property\n    def is_shipping_required(self):\n        return self.cart.is_shipping_required()\n\n    @property\n    def deliveries(self):\n        for partition in self.cart.partition():\n            if self.shipping_method and partition.is_shipping_required():\n                shipping_cost = self.shipping_method.get_total()\n            else:\n                shipping_cost = Price(0, currency=settings.DEFAULT_CURRENCY)\n            total_with_shipping = partition.get_total(\n                discounts=self.cart.discounts) + shipping_cost\n\n            partition = [\n                (item,\n                 item.get_price_per_item(discounts=self.cart.discounts),\n                 item.get_total(discounts=self.cart.discounts))\n                for item in partition]\n\n            yield partition, shipping_cost, total_with_shipping\n\n    @property\n    def shipping_address(self):\n        address = self._get_address_from_storage('shipping_address')\n        if address is None and self.user.is_authenticated():\n            return self.user.default_shipping_address\n        return address\n\n    @shipping_address.setter\n    def shipping_address(self, address):\n        address_data = model_to_dict(address)\n        address_data['country'] = smart_text(address_data['country'])\n        self.storage['shipping_address'] = address_data\n        self.modified = True\n\n    @property\n    def shipping_method(self):\n        shipping_address = self.shipping_address\n        if shipping_address is not None:\n            shipping_method_country_id = self.storage.get(\n                'shipping_method_country_id')\n            if shipping_method_country_id is not None:\n                try:\n                    shipping_method_country = ShippingMethodCountry.objects.get(\n                        id=shipping_method_country_id)\n                except ShippingMethodCountry.DoesNotExist:\n                    return None\n                shipping_country_code = shipping_address.country.code\n                if (shipping_method_country.country_code == ANY_COUNTRY or\n                        shipping_method_country.country_code == shipping_country_code):\n                    return shipping_method_country\n\n    @shipping_method.setter\n    def shipping_method(self, shipping_method_country):\n        self.storage['shipping_method_country_id'] = shipping_method_country.id\n        self.modified = True\n\n    @property\n    def email(self):\n        return self.storage.get('email')\n\n    @email.setter\n    def email(self, email):\n        self.storage['email'] = email\n        self.modified = True\n\n    @property\n    def billing_address(self):\n        address = self._get_address_from_storage('billing_address')\n        if address is not None:\n            return address\n        elif self.user.is_authenticated() and self.user.default_billing_address:\n            return self.user.default_billing_address\n        elif self.shipping_address:\n            return self.shipping_address\n\n    @billing_address.setter\n    def billing_address(self, address):\n        address_data = model_to_dict(address)\n        address_data['country'] = smart_text(address_data['country'])\n        self.storage['billing_address'] = address_data\n        self.modified = True\n\n    @property\n    def discount(self):\n        value = self.storage.get('discount_value')\n        currency = self.storage.get('discount_currency')\n        name = self.storage.get('discount_name')\n        if value is not None and name is not None and currency is not None:\n            amount = Price(value, currency=currency)\n            return FixedDiscount(amount, name)\n\n    @discount.setter\n    def discount(self, discount):\n        amount = discount.amount\n        self.storage['discount_value'] = smart_text(amount.net)\n        self.storage['discount_currency'] = amount.currency\n        self.storage['discount_name'] = discount.name\n        self.modified = True\n\n    @discount.deleter\n    def discount(self):\n        if 'discount_value' in self.storage:\n            del self.storage['discount_value']\n            self.modified = True\n        if 'discount_currency' in self.storage:\n            del self.storage['discount_currency']\n            self.modified = True\n        if 'discount_name' in self.storage:\n            del self.storage['discount_name']\n            self.modified = True\n\n    @property\n    def voucher_code(self):\n        return self.storage.get('voucher_code')\n\n    @voucher_code.setter\n    def voucher_code(self, voucher_code):\n        self.storage['voucher_code'] = voucher_code\n        self.modified = True\n\n    @voucher_code.deleter\n    def voucher_code(self):\n        if 'voucher_code' in self.storage:\n            del self.storage['voucher_code']\n            self.modified = True\n\n    @property\n    def is_shipping_same_as_billing(self):\n        return Address.objects.are_identical(\n            self.shipping_address, self.billing_address)\n\n    def _save_address(self, address, is_billing=False, is_shipping=False):\n        if self.user.is_authenticated() and address.id is None:\n            address = User.objects.store_address(\n                self.user, address, shipping=is_shipping, billing=is_billing)\n        elif address.id is None:\n            address.save()\n        return address\n\n    @transaction.atomic\n    def create_order(self):\n        if self.is_shipping_required:\n            shipping_address = self._save_address(\n                self.shipping_address, is_shipping=True)\n        else:\n            shipping_address = None\n        billing_address = self._save_address(\n            self.billing_address, is_billing=True)\n\n        order_data = {\n            'billing_address': billing_address,\n            'shipping_address': shipping_address,\n            'tracking_client_id': self.tracking_code,\n            'total': self.get_total()}\n\n        if self.user.is_authenticated():\n            order_data['user'] = self.user\n        else:\n            # TODO: we should always save email in order not only\n            # for anonymous\n            order_data['anonymous_user_email'] = self.email\n\n        voucher = self._get_voucher()\n        if voucher is not None:\n            discount = self.discount\n            order_data['voucher'] = voucher\n            order_data['discount_amount'] = discount.amount\n            order_data['discount_name'] = discount.name\n\n        order = Order.objects.create(**order_data)\n\n        for partition in self.cart.partition():\n            shipping_required = partition.is_shipping_required()\n            if shipping_required:\n                shipping_price = self.shipping_method.get_total()\n                shipping_method_name = smart_text(self.shipping_method)\n            else:\n                shipping_price = 0\n                shipping_method_name = None\n            group = order.groups.create(\n                shipping_price=shipping_price,\n                shipping_method_name=shipping_method_name)\n            group.add_items_from_partition(\n                partition, discounts=self.cart.discounts)\n\n        if voucher is not None:\n            Voucher.objects.increase_usage(voucher)\n\n        return order\n\n    def _get_voucher(self):\n        voucher_code = self.voucher_code\n        if voucher_code is not None:\n            vouchers = Voucher.objects.active().select_for_update()\n            try:\n                return vouchers.get(code=self.voucher_code)\n            except Voucher.DoesNotExist:\n                return None\n\n    def recalculate_discount(self):\n        voucher = self._get_voucher()\n        if voucher is not None:\n            try:\n                self.discount = voucher.get_discount_for_checkout(self)\n            except NotApplicable:\n                del self.discount\n                del self.voucher_code\n        else:\n            del self.discount\n            del self.voucher_code\n\n    def get_subtotal(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            total - shipping_cost\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total\n\n    def get_total(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            total\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total if self.discount is None else self.discount.apply(total)\n\n    def get_total_shipping(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            shipping_cost\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total\n\n\ndef load_checkout(view):\n    @wraps(view)\n    @get_or_empty_db_cart\n    def func(request, cart):\n        try:\n            session_data = request.session[STORAGE_SESSION_KEY]\n        except KeyError:\n            session_data = ''\n        tracking_code = analytics.get_client_id(request)\n        \n        checkout = Checkout.from_storage(\n            session_data, cart, request.user, tracking_code)\n        response = view(request, checkout, cart)\n        if checkout.modified:\n            request.session[STORAGE_SESSION_KEY] = checkout.for_storage()\n        return response\n\n    return func\n", "path": "saleor/checkout/core.py"}], "after_files": [{"content": "from __future__ import unicode_literals\nfrom functools import wraps\n\nfrom django.conf import settings\nfrom django.db import transaction\nfrom django.forms.models import model_to_dict\nfrom django.utils.encoding import smart_text\nfrom prices import Price, FixedDiscount\n\nfrom ..cart.views import get_or_empty_db_cart\nfrom ..core import analytics\nfrom ..discount.models import Voucher, NotApplicable\nfrom ..order.models import Order\nfrom ..shipping.models import ShippingMethodCountry, ANY_COUNTRY\nfrom ..userprofile.models import Address, User\n\nSTORAGE_SESSION_KEY = 'checkout_storage'\n\n\nclass Checkout(object):\n\n    VERSION = '1.0.0'\n    modified = False\n\n    def __init__(self, cart, user, tracking_code):\n        self.cart = cart\n        self.storage = {'version': self.VERSION}\n        self.tracking_code = tracking_code\n        self.user = user\n        self.discounts = cart.discounts\n\n    @classmethod\n    def from_storage(cls, storage_data, cart, user, tracking_code):\n        checkout = cls(cart, user, tracking_code)\n        checkout.storage = storage_data\n        try:\n            version = checkout.storage['version']\n        except (TypeError, KeyError):\n            version = None\n        if version != cls.VERSION:\n            checkout.storage = {'version': cls.VERSION}\n        return checkout\n\n    def for_storage(self):\n        return self.storage\n\n    def clear_storage(self):\n        self.storage = None\n        self.modified = True\n\n    def _get_address_from_storage(self, key):\n        address_data = self.storage.get(key)\n        if address_data is not None and address_data.get('id'):\n            try:\n                return Address.objects.get(id=address_data['id'])\n            except Address.DoesNotExist:\n                return None\n        elif address_data:\n            return Address(**address_data)\n        return None\n\n    @property\n    def is_shipping_required(self):\n        return self.cart.is_shipping_required()\n\n    @property\n    def deliveries(self):\n        for partition in self.cart.partition():\n            if self.shipping_method and partition.is_shipping_required():\n                shipping_cost = self.shipping_method.get_total()\n            else:\n                shipping_cost = Price(0, currency=settings.DEFAULT_CURRENCY)\n            total_with_shipping = partition.get_total(\n                discounts=self.cart.discounts) + shipping_cost\n\n            partition = [\n                (item,\n                 item.get_price_per_item(discounts=self.cart.discounts),\n                 item.get_total(discounts=self.cart.discounts))\n                for item in partition]\n\n            yield partition, shipping_cost, total_with_shipping\n\n    @property\n    def shipping_address(self):\n        address = self._get_address_from_storage('shipping_address')\n        if address is None and self.user.is_authenticated():\n            return self.user.default_shipping_address\n        return address\n\n    @shipping_address.setter\n    def shipping_address(self, address):\n        address_data = model_to_dict(address)\n        address_data['country'] = smart_text(address_data['country'])\n        self.storage['shipping_address'] = address_data\n        self.modified = True\n\n    @property\n    def shipping_method(self):\n        shipping_address = self.shipping_address\n        if shipping_address is not None:\n            shipping_method_country_id = self.storage.get(\n                'shipping_method_country_id')\n            if shipping_method_country_id is not None:\n                try:\n                    shipping_method_country = ShippingMethodCountry.objects.get(\n                        id=shipping_method_country_id)\n                except ShippingMethodCountry.DoesNotExist:\n                    return None\n                shipping_country_code = shipping_address.country.code\n                if (shipping_method_country.country_code == ANY_COUNTRY or\n                        shipping_method_country.country_code == shipping_country_code):\n                    return shipping_method_country\n\n    @shipping_method.setter\n    def shipping_method(self, shipping_method_country):\n        self.storage['shipping_method_country_id'] = shipping_method_country.id\n        self.modified = True\n\n    @property\n    def email(self):\n        return self.storage.get('email')\n\n    @email.setter\n    def email(self, email):\n        self.storage['email'] = email\n        self.modified = True\n\n    @property\n    def billing_address(self):\n        address = self._get_address_from_storage('billing_address')\n        if address is not None:\n            return address\n        elif self.user.is_authenticated() and self.user.default_billing_address:\n            return self.user.default_billing_address\n        elif self.shipping_address:\n            return self.shipping_address\n\n    @billing_address.setter\n    def billing_address(self, address):\n        address_data = model_to_dict(address)\n        address_data['country'] = smart_text(address_data['country'])\n        self.storage['billing_address'] = address_data\n        self.modified = True\n\n    @property\n    def discount(self):\n        value = self.storage.get('discount_value')\n        currency = self.storage.get('discount_currency')\n        name = self.storage.get('discount_name')\n        if value is not None and name is not None and currency is not None:\n            amount = Price(value, currency=currency)\n            return FixedDiscount(amount, name)\n\n    @discount.setter\n    def discount(self, discount):\n        amount = discount.amount\n        self.storage['discount_value'] = smart_text(amount.net)\n        self.storage['discount_currency'] = amount.currency\n        self.storage['discount_name'] = discount.name\n        self.modified = True\n\n    @discount.deleter\n    def discount(self):\n        if 'discount_value' in self.storage:\n            del self.storage['discount_value']\n            self.modified = True\n        if 'discount_currency' in self.storage:\n            del self.storage['discount_currency']\n            self.modified = True\n        if 'discount_name' in self.storage:\n            del self.storage['discount_name']\n            self.modified = True\n\n    @property\n    def voucher_code(self):\n        return self.storage.get('voucher_code')\n\n    @voucher_code.setter\n    def voucher_code(self, voucher_code):\n        self.storage['voucher_code'] = voucher_code\n        self.modified = True\n\n    @voucher_code.deleter\n    def voucher_code(self):\n        if 'voucher_code' in self.storage:\n            del self.storage['voucher_code']\n            self.modified = True\n\n    @property\n    def is_shipping_same_as_billing(self):\n        return Address.objects.are_identical(\n            self.shipping_address, self.billing_address)\n\n    def _add_to_user_address_book(self, address, is_billing=False,\n                                  is_shipping=False):\n        if self.user.is_authenticated():\n            User.objects.store_address(\n                self.user, address, shipping=is_shipping,\n                billing=is_billing)\n\n    def _get_address_copy(self, address):\n        address.user = None\n        address.pk = None\n        address.save()\n        return address\n\n    def _save_order_billing_address(self):\n        return self._get_address_copy(self.billing_address)\n\n    def _save_order_shipping_address(self):\n        return self._get_address_copy(self.shipping_address)\n\n    @transaction.atomic\n    def create_order(self):\n        if self.is_shipping_required:\n            shipping_address = self._save_order_shipping_address()\n            self._add_to_user_address_book(\n                self.shipping_address, is_shipping=True)\n        else:\n            shipping_address = None\n        billing_address = self._save_order_billing_address()\n        self._add_to_user_address_book(\n            self.shipping_address, is_billing=True)\n\n        order_data = {\n            'billing_address': billing_address,\n            'shipping_address': shipping_address,\n            'tracking_client_id': self.tracking_code,\n            'total': self.get_total()}\n\n        if self.user.is_authenticated():\n            order_data['user'] = self.user\n        else:\n            # TODO: we should always save email in order not only\n            # for anonymous\n            order_data['anonymous_user_email'] = self.email\n\n        voucher = self._get_voucher()\n        if voucher is not None:\n            discount = self.discount\n            order_data['voucher'] = voucher\n            order_data['discount_amount'] = discount.amount\n            order_data['discount_name'] = discount.name\n\n        order = Order.objects.create(**order_data)\n\n        for partition in self.cart.partition():\n            shipping_required = partition.is_shipping_required()\n            if shipping_required:\n                shipping_price = self.shipping_method.get_total()\n                shipping_method_name = smart_text(self.shipping_method)\n            else:\n                shipping_price = 0\n                shipping_method_name = None\n            group = order.groups.create(\n                shipping_price=shipping_price,\n                shipping_method_name=shipping_method_name)\n            group.add_items_from_partition(\n                partition, discounts=self.cart.discounts)\n\n        if voucher is not None:\n            Voucher.objects.increase_usage(voucher)\n\n        return order\n\n    def _get_voucher(self):\n        voucher_code = self.voucher_code\n        if voucher_code is not None:\n            vouchers = Voucher.objects.active().select_for_update()\n            try:\n                return vouchers.get(code=self.voucher_code)\n            except Voucher.DoesNotExist:\n                return None\n\n    def recalculate_discount(self):\n        voucher = self._get_voucher()\n        if voucher is not None:\n            try:\n                self.discount = voucher.get_discount_for_checkout(self)\n            except NotApplicable:\n                del self.discount\n                del self.voucher_code\n        else:\n            del self.discount\n            del self.voucher_code\n\n    def get_subtotal(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            total - shipping_cost\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total\n\n    def get_total(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            total\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total if self.discount is None else self.discount.apply(total)\n\n    def get_total_shipping(self):\n        zero = Price(0, currency=settings.DEFAULT_CURRENCY)\n        cost_iterator = (\n            shipping_cost\n            for shipment, shipping_cost, total in self.deliveries)\n        total = sum(cost_iterator, zero)\n        return total\n\n\ndef load_checkout(view):\n    @wraps(view)\n    @get_or_empty_db_cart\n    def func(request, cart):\n        try:\n            session_data = request.session[STORAGE_SESSION_KEY]\n        except KeyError:\n            session_data = ''\n        tracking_code = analytics.get_client_id(request)\n\n        checkout = Checkout.from_storage(\n            session_data, cart, request.user, tracking_code)\n        response = view(request, checkout, cart)\n        if checkout.modified:\n            request.session[STORAGE_SESSION_KEY] = checkout.for_storage()\n        return response\n\n    return func\n", "path": "saleor/checkout/core.py"}]}