1 #+TITLE: BOM : Bills-of-materials
2 #+AUTHOR: Christian Fortin
3 #+EMAIL: frozenlock AT gmail DOT com
4 #+OPTIONS: H:3 num:nil toc:t \n:nil ::t |:t ^:t -:t f:t *:t tex:t d:(HIDE) tags:not-in-toc
5 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
6 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
7 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
9 #+CATEGORY: worg-tutorial
11 # This file is released by its authors and contributors under the GNU
12 # Free Documentation license v1.3 or later, code examples are released
13 # under the GNU General Public License v3 or later.
15 [[file:index.org][{Back to Worg's index}]]
20 This add-on collects components across the entire org buffer (even
21 in drawers), making it easy to retrieve and sort data. It uses the
22 column special name as a landmark. We will refer to them as
23 'Keywords'. The keywords are searched using a string-match function,
24 which gives the ability to have multiple column with the same
25 functionality, but also to use the column name as we would usually
26 with org-mode. For example, we can have 'tag' and 'tag2', both are
27 recognized by the BOM add-on and can be used in a spreadsheet-like
28 formula without any confusion. The keywords are also
29 case-insensitive. 'Component' and 'component' will work in the same
32 The BOM is usually used with a dynamic block. (You can use the
33 different functions in emacs-lisp code, but this is beyond the
34 purpose of this tutorial.) Here is the basic dynamic block:
39 And here is what we obtain at this point:
41 : | Section | Tag | Component | Quantity |
42 : |---------+-----+-----------+----------|
45 The table is empty, because we have to either:
46 1. Add keywords in a table;
47 2. Add a line-component.
52 This is the most important keyword and act as the trigger. For
53 this example, let's say we write down things we want to buy. In
54 this case, a new keyboard for our computer. This is how the
62 The '!' character is used in org table to specify column name, such
63 as our keyword, 'component'.
64 And here is what the bill-of-materials for this table is:
67 : | Section | Tag | Component | Quantity |
68 : |-----------+-----+-----------+----------|
69 : | Component | | Keyboard | 1 |
72 As you can see, the heading (Component) was automatically
73 used as the 'section', which doesn't require attention for
74 now. The quantity is, unsurprisingly, 1. There is nothing in the tag
75 column for now, so let's dismiss it by adding the parameter *:no-tag
77 This will results in the following:
78 : #+BEGIN: bom :no-tag t
79 : | Section | Component | Quantity |
80 : |-----------+-----------+----------|
81 : | Component | Keyboard | 1 |
84 Now suppose that our friend too wants a new keyboard.
86 : | | For | Material |
88 : |---+--------+-----------|
90 : | | Friend | Keyboard |
92 : #+BEGIN: bom :no-tag t
93 : | Section | Component | Quantity |
94 : |-----------+-----------+----------|
95 : | Component | Keyboard | 2 |
98 As expected, we get 2 keyboards.
102 The section is used to separate what would otherwise be an
103 identical component. Suppose we don't want our friend's wishes to be
104 in the same BOM as our, but still have them in the same table.
106 : | | For | Material |
107 : | ! | Section | Component |
108 : |---+---------+-----------|
109 : | | Me | Keyboard |
110 : | | Friend | Keyboard |
112 This will results in the following BOM:
114 : #+BEGIN: bom :no-tag t
115 : | Section | Component | Quantity |
116 : |---------+-----------+----------|
117 : | Friend | Keyboard | 1 |
118 : | Me | Keyboard | 1 |
121 Please note that when a component is given a section, it isn't
122 associated with the heading anymore. As an alternative, you can set
123 a ':SECTION:' property in the heading, which will be inherited by
124 all the components _without_ a specified section.
125 Section's priorities are as follow:
127 1. Given section with the 'section' keyword;
128 2. The SECTION property;
133 With this keyword, it is possible to specify a quantity for the
134 associated component. In our always improving scenario, we now want to
135 give a keyboard to another of our friend (as a gift). This is going to
136 be bought at the same time as our keyboard, so they belong together.
138 : | | For | Material | |
139 : | ! | Section | Component | Qty |
140 : |---+---------+-----------+-----|
141 : | | Me | Keyboard | 2 |
142 : | | Friend | Keyboard | 1 |
144 : #+BEGIN: bom :no-tag t
145 : | Section | Component | Quantity |
146 : |---------+-----------+----------|
147 : | Friend | Keyboard | 1 |
148 : | Me | Keyboard | 2 |
151 *Important*: If Qty keyword is present, then any empty field will
152 be considered as _zero_. This way, multiple column quantity are
155 : | | For | Material | Personal | Gift |
156 : | ! | Section | Component | Qty | Qty2 |
157 : |---+---------+-----------+----------+------|
158 : | | Me | Keyboard | 1 | 1 |
159 : | | Friend | Keyboard | 1 | |
161 : #+BEGIN: bom :no-tag t
162 : | Section | Component | Quantity |
163 : |---------+-----------+----------|
164 : | Friend | Keyboard | 1 |
165 : | Me | Keyboard | 2 |
170 When a BOM starts to get big, we often need a quick reminder of
171 why we need certain component. Another use is also to identify the
172 component. As the Qty keyword, multiple Tag columns can be associated
173 with a single component. Here we will simply use the tag as a reminder
174 of what we want to look for in the store.
176 : | | For | Material | Personal | Gift | Need |
177 : | ! | Section | Component | Qty | Qty2 | Tag |
178 : |---+---------+-----------+----------+------+--------------------|
179 : | | Me | Keyboard | 1 | 1 | Matching colors |
180 : | | Friend | Keyboard | 1 | | Dinosaurs pictures |
182 To show the tag column in the BOM, we simply remove the no-tag
185 : | Section | Tag | Component | Quantity |
186 : |---------+--------------------+-----------+----------|
187 : | Friend | Dinosaurs pictures | Keyboard | 1 |
188 : | Me | Matching colors | Keyboard | 2 |
192 If two Tag columns are present for a single Component column, the
193 tags will be associated with this component, separated by a comma.
195 * Renaming BOM columns
197 It is possible to rename the BOM columns with the following
203 - col-name-description
206 This is how our renamed BOM would look like:
208 : #+BEGIN: bom :col-name-section For :col-name-tag Need :col-name-component Stuff :col-name-quantity Qty
209 : | For | Need | Stuff | Qty |
210 : |--------+--------------------+----------+-----|
211 : | Friend | Dinosaurs pictures | Keyboard | 1 |
212 : | Me | Matching colors | Keyboard | 2 |
215 * Multiple component's column
217 There is two way to add components in a section. Either by adding
218 other rows with the same section's name, or by adding other
219 columns. Both have their uses and they should come to you quite
220 naturally. In our example, we want more stuff.
222 : | | For | Material | Personal | Gift | Need | Stuff | More stuff | Much more stuff | How many |
223 : | ! | Section | Component | Qty | Qty2 | Tag | Component | Component | Component | Qty |
224 : |---+---------+-----------+----------+------+--------------------+-----------+------------+-----------------+----------|
225 : | | Me | Keyboard | 1 | 1 | Matching colors | Mouse | Headset | USB flash drive | 23 |
226 : | | Friend | Keyboard | 1 | | Dinosaurs pictures | | | | |
227 : | | Friend | | | | | | | CDs | 50 |
228 : | | Friend | Mouse | 1 | | | | | | |
230 This is beginning to get interesting. Note that even if we can
231 name the additional columns 'Component2' or 'ComponentAAA',
232 there's no use to do it if no table-formula uses the column
235 * Precise section selection
236 Now suppose we want to get OUR to-buy list. Simply specify
237 the section's parameter *:section Me*:
239 : #+BEGIN: bom :section Me
240 : | Tag | Component | Quantity |
241 : |-----------------+-----------------+----------|
243 : | Matching colors | Keyboard | 2 |
245 : | | USB flash drive | 23 |
248 Wait, where's the section column? Well we don't need it anymore,
251 A '+' sign will specify we want more than a single section. *:section
252 Me+Friend* will select both section, and add the quantity and tags
255 : #+BEGIN: bom :section Me+Friend
256 : | Tag | Component | Quantity |
257 : |-------------------------------------+-----------------+----------|
260 : | Dinosaurs pictures, Matching colors | Keyboard | 3 |
262 : | | USB flash drive | 23 |
265 *Do not* put a whitespace between the section name and the '+' sign.
266 Speaking of whitespace, if you need one in a section name, simply
268 : #+BEGIN: bom :section "Section with whitespace"
270 We can also return every section that matches at least what we
271 provide. To activate this, use *:part-match t*. With this, if we
272 write "fr", the Friend section is returned. If we had another
273 section named "Frosting", than Friend and Frosting would have been
274 merged and we would have a total for both section.
276 : #+BEGIN: bom :section fr :part-match t
277 : | Tag | Component | Quantity |
278 : |--------------------+-----------+----------|
280 : | Dinosaurs pictures | Keyboard | 1 |
284 It is also possible to specify that we don't want any section
285 containing "fr". For this, use the parameter *:remove t*.
287 : #+BEGIN: bom :section fr :part-match t :remove t
288 : | Tag | Component | Quantity |
289 : |-----------------+-----------------+----------|
291 : | Matching colors | Keyboard | 2 |
293 : | | USB flash drive | 23 |
296 In this case, getting all sections not containing "fr" is the
297 equivalent of choosing the section "Me".
299 If you simply want the components from the current heading, use the
300 parameter *:local-only t*. This will return components with the
301 current heading as their section, which is the default of every
302 component if no section is provided. If a section has been provided to
303 a component (and is not exactly equal to the heading), the component
304 will not be returned.
306 Here, we don't have any component under this heading:
307 : #+BEGIN: bom :local-only t
308 : | Tag | Component | Quantity |
309 : |-----+-----------+----------|
313 This is all really interesting, but when we're in a shop, we want
314 to know how many of each item we have to buy, we need a *total*.
315 For this, simply add the *:total t* parameter. We will also remove
316 the tags once again by using *:no-tag t*.
318 : #+BEGIN: bom :total t :no-tag t
319 : | Component | Quantity |
320 : |-----------------+----------|
325 : | USB flash drive | 23 |
328 This is the equivalent of merging every sections together.
329 * Adding a component without a table
331 There is another option you might need. If you ever want to
332 add a component without a table, use the #+BOM commentary. As any
333 other org-mode commentary, this one won't appear when exported to
334 another document (pdf, html, docbook..). It will, however, enable
335 you to add a single component in the bill-of-materials. Here is an
337 : #+BOM: Keyboard :section Need :tag "Matching colors"
339 As with the table components, you can simply give a component name if
340 you desire. If no section is given, it will be inherited as an
341 ordinary component in a table: a section property or the current
345 There is two way to add details to a BOM. The first one is to setq
346 `org-bom-details' with a plist containing, depending on your
347 needs, :description, :datasheet-pdf and :price. You must, however, at
348 least have the component name in the :name property. Here is an
349 example on how to set this variable:
351 #+BEGIN_SRC emacs-lisp
352 (setq org-bom-details '((:name "Keyboard" :description
353 "Something" :price "40")
354 (:name "CDs" :description "Not
355 DVDs" :datasheet-pdf "CD.pdf")))
357 Please note that the price is a *string*.
359 The other method, valid for the current buffer only, is to give one
360 or more bom-details table. It is recognized when a table is named as
362 : #+TBLNAME: bom-details
364 Once again, the column names are used. Contrary to the normal BOM
365 keywords however, these are case-sensitive and must be written
366 exactly as their property name. For example, the column of the
367 property ':name' must be 'name'.
368 : #+TBLNAME: bom-details
369 : | ! | name | description | price |
370 : |---+----------+--------------+-------|
371 : | | Keyboard | Used to type | 40 |
374 Any bom-details table will temporarily overshadow the
375 `org-bom-details' variable, but won't erase or modify it. This means
376 you can safely use a bom-details table if you need to change some
377 local buffer description, while using `org-bom-details' in multiple
380 Look at the CDs description. When a field is empty, it is *not* used
381 and BOM falls back to the property in the `org-bom-details'
386 You can add a description column in a BOM by adding the
387 *:description t* parameter.
389 : #+BEGIN: bom :total t :no-tag t :description t
390 : | Component | Quantity | Description |
391 : |-----------------+----------+--------------|
392 : | CDs | 50 | Not DVDs |
393 : | Headset | 1 | N/A |
394 : | Keyboard | 3 | Used to type |
395 : | Mouse | 2 | N/A |
396 : | USB flash drive | 23 | N/A |
399 See how the CDs' description wasn't the empty field from the
404 You can add a price column in a BOM by adding the *:price t*
407 : #+BEGIN: bom :total t :no-tag t :description t :price t
408 : | Component | Quantity | Price | Description |
409 : |-----------------+----------+-------+--------------|
410 : | CDs | 50 | | Not DVDs |
411 : | Headset | 1 | | N/A |
412 : | Keyboard | 3 | 120 | Used to type |
413 : | Mouse | 2 | | N/A |
414 : | USB flash drive | 23 | | N/A |
415 : |-----------------+----------+-------+--------------|
416 : | TOTAL: | | 120 | |
417 : #+TBLFM: @>$3=vsum(@I..@>>)
419 The price is automatically multiplied by the quantity of each
420 component. In addition, a total row is added at the table's bottom
421 with a vertical sum formula.
425 This is a special property and must be used only if you intend to
426 export in a pdf document. See [[LaTeX mode and bom-datasheet]] for more details.
428 * List of BOM parameters
429 Here is a list of all the parameters usable in a BOM dynamic block,
430 as seen throughout this tutorial:
432 - no-tag :: Remove the tags column
433 - section :: Select this section (or more if there's a + sign)
434 - part-match :: Select every section with at least the string
435 provided for the section parameter
436 - remove :: Select every sections except the one(s) provided
437 - descripton :: Add the description column
438 - price :: Add the price column and a total row at the bottom of the
440 - col-name-*** :: Rename the associated column
441 * Advanced and elisp functions
443 Each BOM dynamic block scans the entire buffer individually. While
444 it is necessary that each block be able to update itself, it
445 becomes a waste when the command `org-update-all-dblocks' is
446 used. (The components usually aren't changing from a dblock evaluation to
449 In order to speed up updates, there's a variable that can be used
450 to stop each BOM dblock from doing a buffer-wide scan. To disable the
451 scans, set `org-bom-update-enable' to nil.
453 The author uses a function similar to this one to speed up updates:
454 #+BEGIN_SRC emacs-lisp :exports code
455 (defun reg-update-project (&optional latex-mode)
456 "Update every table and dynamic block in the buffer. If latex-mode
457 is non-nil, various latex commands will be inserted."
459 (org-table-iterate-buffer-tables)
460 (org-bom-total); manually update the BOM database
461 (let ((org-bom-update-enable nil)
462 (org-bom-latex-mode latex-mode)
463 (org-bom-details (copy-tree org-bom-details)));so we don't overwrite
464 (org-bom-check-for-details-table); manually update `org-bom-details'
465 (org-update-all-dblocks))
466 (message "Project updated"))
469 ** LaTeX mode and bom-datasheet
470 This mode isn't fully integrated to org-mode and should be seen as a
471 hack. It is however useful to the author, which is why it is
474 Set the `org-bom-latex-mode' variable to non-nil in order to
475 activate the latex-mode. If set, all BOM dynamic block will insert
478 These commands targets:
479 - Tags :: When there is more tags than `org-bom-latex-max-tags' per
480 component, the remaining tags are put in a pdf comment.
481 - Component name :: If a datasheet exists for the component, its
482 name will become a link to its datasheet.
485 If you ever activate the LaTeX mode, use the bom-datasheet dynamic
486 block at the end of your document. The optional parameter
487 *:description t* will add a summary of all the components used in
488 this buffer with their description, just before the datasheets.
490 : #+BEGIN: bom-datasheet
492 : #+LaTeX: \includepdf[pages=-,landscape=true,addtotoc={1, subsection, 1, CDs,CD.pdf}]{\DATASHEETPATH/CD.pdf}
496 As you may have noticed, there's a LaTeX variable in this command:
497 \DATASHEETPATH. In order to work, you must set this variable
500 : #+LATEX_HEADER: \newcommand{\DATASHEETPATH}{Name-of-the-folder/}'
502 Name-of-the-folder is the folder where the datasheets' files