4. Recording sales and the report

Lines 50–63

The last two methods of the class deal with sales. sell lowers a product's stock and records the sale; report adds up every recorded sale to give the total quantity sold and the total revenue.

Code for this part

shop_manager.py, lines 50–63
50    def sell(self, name, qty):51        if name not in self.products:52            raise ValueError('کالا پیدا نشد')53        if qty <= 0:54            raise ValueError('تعداد باید مثبت باشد')55        product = self.products[name]56        if qty > product['qty']:57            raise ValueError('موجودی کافی نیست')58        product['qty'] -= qty59        self.sales.append({'name': name, 'qty': qty, 'total': qty * product['sell']})60        self.save()61 62    def report(self):63        return sum(s['qty'] for s in self.sales), sum(s['total'] for s in self.sales)

Click a line number to jump to its explanation; click “line …” in an explanation to highlight that line in the code.

Line by line

line 50def sell(self, name, qty):
sell takes a product name and a quantity. It is called from the module-level function sell (line 103).
line 51if name not in self.products:
The product must exist.
line 52raise ValueError('کالا پیدا نشد')
Raises “product not found”.
line 53if qty <= 0:
The quantity must be greater than zero. Unlike line 31, zero is rejected too, since selling nothing makes no sense.
line 54raise ValueError('تعداد باید مثبت باشد')
Raises “quantity must be positive”.
line 55product = self.products[name]
Stores the product's dictionary in product. This is not a copy but a reference to the same dictionary inside self.products, so any change to product changes the original data.
line 56if qty > product['qty']:
If more is requested than there is in stock…
line 57raise ValueError('موجودی کافی نیست')
…raise “not enough stock”.
line 58product['qty'] -= qty
-= means “subtract from itself”: the stock goes down by the quantity sold. Because product is a reference (line 55), self.products changes too.
line 59self.sales.append({'name': name, 'qty': qty, 'total': qty * product['sell']})
Appends a sale record to the list. total uses the sale price at this moment, so editing the price later (line 41) does not change the totals of past sales.
line 60self.save()
Saves both changes (stock and sale) to disk.
line 61
A blank line.
line 62def report(self):
report takes no arguments and only calculates. It is called from the module-level function report (line 110).
line 63return sum(s['qty'] for s in self.sales), sum(s['total'] for s in self.sales)
Sums two generator expressions: the qty of every sale and the total of every sale. Two values separated by a comma form a tuple, for example (2, 80000000000.0).

Note

Example: if sales holds {'qty': 2, 'total': 30} and {'qty': 5, 'total': 100}, report() returns (7, 130).

How it connects to the rest of the code

See also

Shop.sell
The “record sale” button: function sell line 102 → shop.sell(name, quantity) line 103.
Shop.report
The “sales report” button: function report line 109 → line 110, which unpacks the tuple into count and income and shows them on line 111.
self.sales
Created in the constructor line 11 and filled from the file in load line 24.
product['qty']
The reduced stock is shown in the table again by refresh on line 87 (line 118).

Check your understanding

Try to answer each question before opening it.

  1. Why does product['qty'] -= qty on line 58 also change self.products?

    Show answer

    product on line 55 is a reference to the same dictionary, not a copy.

  2. Selling 0 items fails, but a price of 0 is accepted. Which lines decide this?

    Show answer

    line 53 uses qty <= 0 for sales; line 31 only rejects values below 0 for prices.

  3. What does report() return when there are no sales yet?

    Show answer

    (0, 0), because sum of an empty sequence is 0.

  4. If a price is edited after a sale, does that sale's total change?

    Show answer

    No. line 59 stores the total at the moment of the sale.