Lo fundamental para resolver eso es tratar de sistematizar lo mínimo. Y lo mínimo, es tener un ID de Orden de trabajo y una codificación automática. Pero necesitas dos tablas:
1) Cabecera: ID,Fecha, Datos Vehículo, Datos del Titular, Condiciones, Facturado
2) Detalle: ID,Grupo,FEcha,Concepto,Cantidad,Precio,Importe, etc
El ID es un consecutivo, numérico o alfanumérico.
El grupo, apunta al punto de entrada de datos (control grid respectivo)
De esa forma, puedes buscar en la tabla de cabecera por los datos esenciales (fecha, patente, titular, etc) y relacionar con la tabla de detalle por el ID.
Conocido el ID, tendrás 3 búsquedas:
SELECT * FROM DETALLE WHERE ID=lnId AND GRUPO = "R" into cursor Repuestos readwirte
SELECT * FROM DETALLE WHERE ID=lnId AND GRUPO = "L" into cursor Lubricantes readwrite
SELECT * FROM DETALLE WHERE ID=lnId AND GRUPO = "T" into cursor Torneria readwrite
Luego va la asociación a los controles grid (recomiendo una rutina específica para ello), pero enunciado genéricamente:
Thisform.grdRepuestos.RecordSource = "Repuestos"
Thisform.grdLubricantes.RecordSource = "Lubricantes"
thisform.grdTorneria.REcordSource = "Torneria"
Para guardar los datos, tendrás que guardar los datos de los tres cursores en la tabla Detalle.
Tratándose de tablas dbf, te puedes basar en el número de registro (RECNO()) de la tabla principal. Por ejemplo;:
SELECT *,recno() as tb_record, .F. as tb_delete FROM DETALLE WHERE ID=lnId AND GRUPO = lcGrupo into Cursor &lcCursor READWRITE
Luego, tienes que tener en cuenta:
1) Si el registro es nuevo (se agregó en el control grid), tb_record tendrá el valor Cero.
2) Si el registro existía cuando se creó el cursor, tb_record tendrá el número de registro de la tabla.
3) Para borrar un registro, antes de DELETE, tiene que ir REPLACE tb_delete with .T.
A la hora de guardar en la tabla detalle, tienes que ver las siguientes situaciones:
1) Si tb_record = 0, indica que se intentó un alta. Pero si tb_delete = .T., el alta se ignora porque significa que la anuló el usuario.
2) Si tb_Record > 0, indica que se pudo haber modificado el registro. Si tb_delete = .T., significa que el usuario quiere eliminar ese registro
Te quedaría más o menos así:
*<Guardando la información>
lcSetDeleted = SET("Deleted")
SET DELETED OFF
lcCursor_list = "Repuestos,Lubricantes,Torneria"
lcGrupo_List = "R,L,T"
lnIdTrabajo = Cabecera.Id_Orden
FOR i = 1 TO GETWORDCOUNT(m.lcCursor_List,",")
lcCursor = GETWORDNUM(m.lcCursor_List,i,",")
lcGrupo = GETWORDNUM(m.lcGrupo_List,i,",")
SELECT (m.lcCursor)
*<Incluye el ID del trabajo y el grupo de pertenencia>
replace ALL ID WITH m.lnIdTrabajo,;
grupo WITH m.lcGrupo
SCAN
SCATTER TO laDatos
DO CASE
CASE EMPTY(tb_record) && si no está borrado, es un alta
IF !tb_Delete
INSERT INTO detalle FROM ARRAY laDatos
ENDIF
CASE tb_record > 0 && es una modificación.
lnGo = tb_record
lDelete = tb_delete
SELECT detalle
GO m.lnGo
IF RLOCK()
IF m.lDelete && si está borrado hay que borrarlo en la tabla
DELETE
ELSE
GATHER FROM laDatos
ENDIF
ENDIF
ENDCASE
UNLOCK IN detalle
ENDSCAN
NEXT
SET DELETED &lcSetDeleted && reestablece la configuración
*<Limpia los 3 cursores>
FOR i = 1 TO GETWORDCOUNT(m.lcCursor_List,",")
lcCursor = GETWORDNUM(m.lcCursor_List,i,",")
SELECT (m.lcCursor)
ZAP
NEXT
WITH thisform
.grdRepuestos.refresh
.grdLubricantes.refresh
.grdTorneria.refresh
ENDWITH
Antes de guardar, debes hacer el proceso de validación. Eliminar los registros con campos para los que no se admite un valor vacío, etc.
Esto es fundamental para no guardar registros inútiles en la tabla, ya que si se opera directamente en el control grid, el usuario puede crear una multitud de registros vacíos con solo intentar desplazarse hacia abajo.
Si sigues este método, recuerda que, para borrar un registro en el cursor:
SELECT (lcCursor)
REPLACE tb_delete with .T.
DELETE && esto es necesario para que el usuario deje de ver el registro borrado
Thisform.grdElqueSea.refresh
Viendo tu pantalla, te aconsejo que los Grid tengan la propiedad DeleteMark = .F. Y agrega un botón para borrar, con consulta previa.