Timesheets in Sales Invoice (again)

Hi,

I tried to enable timesheet printing in Sales invoices. With some additional features.

with the help of this post: https://discuss.frappe.io/t/sales-invoice-from-timesheet-function/26856/6

I created an aditional block in our invoices. This works, more or less. We need to have at least the date in the list, when this activity happend.

{% if doc.timesheets and doc.print_timesheets -%}

<div class="page-break"></div>

<h4>{{ _("Timesheets") }}</h4>
<table class="table table-bordered table-condensed">
	<thead>
		<tr>
			<th style="width: 40px" class="table-sr">{{ _("Sr") }}</th>
			<th style="width: 150px;">
				{{ _("Date") }}</th>
			<th style="width: 300px;">
				{{ _("Note") }}</th>
			<th style="width: 80px;" class="text-right">
				{{ _("Hours") }}</th>
		</tr>
	</thead>
	<tbody>
		{% for ts in doc.timesheets %}
		<tr>
			<td class="table-sr">{{ loop.index }}</td>
			<td>
				<div class="value">
					{{ frappe.utils.formatdate(ts.from_time) }} - {{ ts.start_date }}
				</div>
			</td>
			<td>
				<div class="value">
				{{ ts.activity_type }} - {{ ts.description }}
				</div>
			</td>
			<td class="text-right">
				<div class="value">
					{{ ts.billing_hours | round(2, 'ceil') }}
				</div>
			</td>
		</tr>
		{% endfor %}
	</tbody>
</table>
{% endif %}

I guess, I need to have start date/time in this list to get it print. But I was not able to do this, because start date is comming from a child - child table.

Thank you

Hi @Andreas_Thuer, there have been some changes to sales invoice timesheet between v12 and v13. What version are you using?

1 Like

Hi @rmeyer,

we are using V13.8 on the way to 13.9

Thank you

hi @rmeyer,
do you have any helpfull information for us?
Thank you
Andreas

Maybe try something like this. Couldn’t test it yet, but the from_time is not in the sales invoice timesheet table by default, so we’ll have to fetch it explicitly.

You can check which fields are available here.

{% if doc.timesheets and doc.print_timesheets -%}

<div class="page-break"></div>

<h4>{{ _("Timesheets") }}</h4>
<table class="table table-bordered table-condensed">
	<thead>
		<tr>
			<th style="width: 40px" class="table-sr">{{ _("Sr") }}</th>
			<th style="width: 150px;">
				{{ _("Date") }}</th>
			<th style="width: 300px;">
				{{ _("Note") }}</th>
			<th style="width: 80px;" class="text-right">
				{{ _("Hours") }}</th>
		</tr>
	</thead>
	<tbody>
		{% for ts in doc.timesheets %}
                {% set from_time = frappe.get_value("Timesheet Detail", ts.timesheet_detail, "from_time") %}
		<tr>
			<td class="table-sr">{{ loop.index }}</td>
			<td>
				<div class="value">
					{{ frappe.utils.get_formatted(from_time, "dd.mm.yyyy") }}
				</div>
			</td>
			<td>
				<div class="value">
				{{ ts.activity_type }} - {{ ts.description }}
				</div>
			</td>
			<td class="text-right">
				<div class="value">
					{{ ts.billing_hours | round(2, 'ceil') }}
				</div>
			</td>
		</tr>
		{% endfor %}
	</tbody>
</table>
{% endif %}

This should work as well:

    {% if doc.timesheets -%}
    <div style="page-break-after: always"></div>
    <div class="timesheets">
        <h3>Zeiterfassung</h3>
        <div data-fieldname="timesheets" data-fieldtype="Table">
            <table class="w-100 table table-bordered table-condensed">
                <thead>
                    <tr>
                        <th style="width: 10%" class="table-sr">{{ _("Sr") }}</th>
                        <th style="width: 20%;" class="" data-fieldname="timesheets" data-fieldtype="Table">
                            {{_("Date")}}</th>
                        <th style="width: 20%;" class="" data-fieldname="timesheets" data-fieldtype="Table">
                            {{_("Employee")}}</th>
                        <th style="width: 40%;" class="" data-fieldname="timesheets" data-fieldtype="Table">
                            {{_("Note")}}</th>
                        <th style="width: 10%;" class="text-right" data-fieldname="timesheets" data-fieldtype="Table">
                            {{_("Hours")}}</th>
                    </tr>
                </thead>
                <tbody>
                    {% for ts in doc.timesheets %}
                    {% set timesheet = frappe.get_doc("Timesheet", ts.time_sheet)%}
                        {% if ts.billing_hours > 0 %}
                            <tr style="page-break-inside: avoid;">
                                <td class="table-sr">{{ loop.index }}</td>
                                <td class="" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ timesheet.get_formatted('start_date') }}
                                    </div>
                                </td>
                                <td class="" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ timesheet.employee_name }}
                                    </div>
                                </td>
                                <td class="" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ ts.description }}
                                    </div>
                                </td>
                                <td class="text-right" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ "{:.2f}".format(ts.billing_hours) | replace(".", ",") }}
                                    </div>
                                </td>
                            </tr>
                        {% endif %}
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
    {%- endif %}

This approach takes the start date and employee name from the timesheet and the description and time from the timesheet detail.

It also doesn’t check for the custom field “print timesheets”. You can add that to the if-condition if needed.

1 Like

I have to write again. I am also in the process of adding time tracking to the invoice. However, I do not get the activity_type output. Unfortunately I need this. Can someone help me?

  {% for ts in doc.timesheets | sort(attribute='start_date') %}
                    {% set timesheet = frappe.get_doc("Timesheet", ts.time_sheet)%}
                     <!--   {% if timesheet.total_billable_amount > 0 and timesheet.customer_note %} -->  <!--  {% endif %} -->
                            <tr style="page-break-inside: avoid;">
                                 <td class="" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ frappe.utils.formatdate(timesheet.start_date, "dd.mm.yyyy") }}
                                    </div>
                                </td>
                                <td class="" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ timesheet.note }}
                                    </div>
                                </td>
                                <td class="text-right" data-fieldname="timesheets" data-fieldtype="Table">
                                    <div class="value">
                                        {{ "{:.2f}".format(timesheet.total_billable_hours) | replace(".", ",") }}
                                    </div>
                                </td>
                                 <td >
                                    <div class="value">
                                          {{ timesheet.activity_type }}
                                        
                                         {{ timesheet.employee_name }}
                                    </div>
                                </td>
                            </tr>
                     {% endfor %}